Abstract classes and static methods

Hello there!

I was wondering why is not possible to define a static method into an abstract class, well... I was wondering that because I'm trying to force an user who inherits from this class to implement a static class and I just didn't find a way to do that... is there a way to do this

Because there's a layer calling static methods and I don't want to change these methods to regular methods just because I'm not being able to solve this problem...

Thanks




Answer this question

Abstract classes and static methods

  • Sree2000

    Static methods can be defined in an abstract class. However, you cannot force a derived class to implement a static method. If you think about it, such a method would be useless. Static methods are invoked using type names, not instance variables. If I call MyBaseClass.MyMethod, then MyBaseClass.MyMethod will always be invoked. How would it do you any good to force MyChildClass, which inherits from MyBaseClass, to also a implement a static MyMethod

  • Ahmed Sajwani

    If all you need is semantic separation, just name the methods differently.

    You dont have to create an instance to get the default values; you just have to have two different versions of the member, one static and one instance, as I demonstrated above.

    Luke Breuer wrote:

    I am curious, why must inheritance apply to instances but not types themselves

    Because without an instance of an object, how do you know which derived class to look for Polymorphism is based on the fact that a reference of type Foo may point to an object which is actually of type Bar; but if you dont have an instance to operate on (which is the case when using static members), and there are many objects which derive from Foo, and you call the static member Foo.MemberA, what information do you have to determine which derived class's MemberA should be called

    Try this: write out the sample syntax that you would like to be able to implement involving the inheritance of static members. I think you will quickly discover that there is no viable way to do it. If you find something that you think would work, post it, and we can talk about it.



  • CharlesJ

    Would it not be useful to have the C# compiler force derived classes to implement certain static methods This would strictly be a contract-enforcing feature.

  • Varsha Sharma

    Using your recommendations, there is no semantic way to separate the SelectSql method that is not specific to one instance (e.g., returns all records matching some criteria), and the SelectSql that is specific to one instance (e.g., select this record out of the database). Static methods provide this semantic separation.

    According to your recommendations, I have to construct an instance in order to get the default properties for the class; this seems to be a round-about method. The instance I would be constructing would be superfluous; what does it gain me

    Static inheritance would be part-member hiding: there would be no upwards (base --> derived) vtable, only some sort of vtable whereby if I call a static overridden member on a derived class, when it invokes a static member without a type, it passes the appropriate type information to the base class. Does this not make sense

    I am curious, why must inheritance apply to instances but not types themselves What is so flawed about instanceless members being inheritable Is this because you can't have upwards invokation (Again, this would mean that a derived class effectively hides the base class's abstract/virtual static members; calling a static abstract/virtual method on an abstract class would result in an exception.)

  • Philip Jaques

    I am writing a database layer and would like abstract static methods for the RecordBase class. Every derived class should implement the static property/function DefaultDataSource, as most instances of any derived class will use its default data source. It would be useful to require derived classes to implement this static method, but there is no such option.

    There would be no way to polymorphically access said static member; the only way to access it without specifically naming the derived class would be to iterate through all derived classes and invoke the static member via reflection.

  • Raj KKK

    Luke Breuer wrote:
    Within Foo, one is allowed to access Yay from a static method, but any code that touches said static method must be invoked in Bar. In other words, whenever Yay is invoked in Foo, the first non-Foo member on the stack must be a Bar member.

    I dont understand what it is that you are trying to accomplish here; even if what you are describing were implemented, how would it be at all beneficial It doesnt seem like it would get you any closer to solving the problem that you initially described. Can you post example code of how this would look if it were available I think I need to see what you are describing to understand it.



  • fly_eye

    I've rolled a database access layer and I'm trying to keep all the logic local to a given table inside the class that wraps it. However, my current implementation requires me to put code in derived classes that I would rather put in the base class. For example, DefaultDataSource; if the base class could reference that static property, without a type, and have that reference hit the derived class's value, I could put a good amount of code in the base class. Currently, I feel that going "part-way", or putting as much as I can in the base class, will break the logical flow of the class.

    Perhaps someone has some insight


    /// <summary>
    /// A wonderful class that wraps the SQL Server table dbo.Fool.
    /// </summary>
    /// <remarks>
    /// Automatically generated message: this class was not designed to be inherited from, hence it being sealed.
    /// More work needs to be done on the code generation if these classes need to be inheritable.
    /// </remarks>
    public sealed class Fool : DalRecordBase
    {
    #region Private Enums
    private enum FoolColumns
    {
    Description,
    Cool
    }
    #endregion

    #region Private Database Fields
    // read only
    [SqlDbType("fool_pk", SqlDbType.Int, -1, false, false)]
    private int _pk;
    [SqlDbType("input_date", SqlDbType.DateTime, -1, false, true)]
    private DateTime _inputDate;

    // writable
    [SqlDbType("description", SqlDbType.VarChar, 100, false, false)]
    private string _description;
    [SqlDbType("cool", SqlDbType.Bit, -1, false, false)]
    private bool _cool;
    #endregion

    #region Private Fields
    private BitArray _changedColumns;
    #endregion

    #region Public Database Properties
    public int Pk
    {
    get { return _pk; }
    }

    public DateTime InputDate
    {
    get { return _inputDate; }
    }

    public string Description
    {
    get { return _description; }
    set
    {
    if (_description != value)
    {
    SetColumnChanged(FoolColumns.Description);
    _description = value;
    }
    }
    }

    public bool Cool
    {
    get { return _cool; }
    set
    {
    if (_cool != value)
    {
    SetColumnChanged(FoolColumns.Cool);
    _cool = value;
    }
    }
    }
    #endregion

    #region Public Property Overrides
    public override bool DataChanged
    {
    get { return _changedColumns != null; }
    }
    #endregion

    #region Public Constants
    public static readonly DalDataSourceInfo DefaultDataSource = new DalDataSourceInfo("dbo", "Fool", DataSourceType.Table);
    public const int NumberOfWritableColumns = 2;
    #endregion

    #region Public Constructors
    public Fool()
    : base(Fool.DefaultDataSource)
    { }

    public Fool(SqlDataReader dr)
    : base(Fool.DefaultDataSource)
    {
    this.ExistsInDatabase = true;

    int i = -1;

    _pk = dr.GetInt32(++i);
    _inputDate = dr.GetDateTime(++i);
    _description = dr.GetString(++i);
    _cool = dr.GetBoolean(++i);
    }
    #endregion

    #region SelectSql
    public static string SelectSql(int pk, string description)
    {
    string sql =
    "select \n" +
    " fool_pk,\n" +
    " input_date,\n" +
    " description,\n" +
    " cool\n" +
    "from " + Fool.DefaultDataSource.ToString();

    string where = "\nwhere\t";

    if (pk != -1)
    where += "fool_pk = " + pk + " and\n\t\t";
    if (description != null)
    where += "description = " + DalBase.ToSqlString(description) + " and\n\t\t";

    if (where != "\nwhere\t")
    sql += where.Substring(0, where.Length - " and\n\t\t".Length);

    return sql;
    }
    #endregion

    #region InsertSql
    public string InsertSql()
    {
    if (this.ExistsInDatabase)
    throw new InvalidOperationException("This Fool instance already exists in the database.");

    string fields = "";
    string values = "";

    foreach (KeyValuePair<string, string> kvp in this.GetChangedColumns())
    {
    fields += "\t\t" + kvp.Key + ",\n";
    values += "\t\t" + kvp.Value + ",\n";
    }

    fields = fields.Remove(fields.LastIndexOf(','), 1);
    values = values.Remove(values.LastIndexOf(','), 1);

    return
    "insert into " + this.DataSourceInfo.ToString() + "\n" +
    " (\n" +
    fields +
    " ) values (\n" +
    values +
    " )";
    }
    #endregion

    #region UpdateSql
    public string UpdateSql()
    {
    if (!this.ExistsInDatabase)
    throw new InvalidOperationException("This Fool instance does not exist in the database.");
    if (!this.DataChanged)
    throw new InvalidOperationException("This Fool instance has not changed, so it cannot be updated.");

    string sql = "update\t" + this.DataSourceInfo.ToString() + " set\n";

    foreach (KeyValuePair<string, string> kvp in this.GetChangedColumns())
    sql += "\t\t" + kvp.Key + " = " + kvp.Value + ",\n";

    sql = sql.Remove(sql.LastIndexOf(','), 1) + "where\t" + Fool.DefaultDataSource.ObjectName.ToLower() + "_pk = " + _pk;

    return sql;
    }
    #endregion

    #region DeleteSql
    public string DeleteSql()
    {
    if (!this.ExistsInDatabase)
    throw new InvalidOperationException("This Fool instance does not exist in the database.");

    string sql =
    "delete from " + this.DataSourceInfo.ToString() + "\n" +
    "where " + Fool.DefaultDataSource.ObjectName.ToLower() + "_pk = " + _pk;

    return sql;
    }
    #endregion

    #region Public Methods
    /// <summary>
    /// Marks this instance as not changed; call this after executing <code cref="UpdateSql">UpdateSql</code>.
    /// </summary>
    public void ClearChanged()
    {
    _changedColumns = null;
    }
    #endregion

    #region Private Methods
    private void SetColumnChanged(FoolColumns column)
    {
    if (_changedColumns == null)
    _changedColumns = new BitArray(Fool.NumberOfWritableColumns);

    _changedColumns[(int)column] = true;
    }

    private KeyValuePair<string, string>[] GetChangedColumns()
    {
    int numChanged = 0;

    foreach (bool b in _changedColumns)
    if (b)
    numChanged++;

    KeyValuePair<string, string>[] fields = new KeyValuePair<string, string>[numChanged];

    int curIdx = 0;

    if (_changedColumns[(int)FoolColumns.Description])
    fields[curIdx++] = new KeyValuePair<string, string>("description", DalBase.ToSqlString(_description));
    if (_changedColumns[(int)FoolColumns.Cool])
    fields[curIdx++] = new KeyValuePair<string, string>("cool", (_cool "1" : "0"));

    return fields;
    }
    #endregion

    #region Public Method Overrides
    public override ReadOnlyCollection<ValidationError> ValidateData()
    {
    throw new NotImplementedException("This method stub was automatically generated and needs to be replaced!");
    }
    #endregion
    }


  • Techno_Dex

    Just because a property is the "default value" for a class, or does not use any instance data, does not necessarily mean it should be static. Static members should only be used when they apply ONLY to the class in which they are defined. But DefaultDataSource does not apply just to the type in which it is defined; it is part of the inheritance design of your entire system, and it is used in the context of polymorphism to allow your base class to do its work. Therefore it, and the SelectSql method, should be instance members, even if they return a static value. For example:



    private static string _DefaultDataSource = "MyDataSource";
    public overrides string DefaultDataSource
    {
    get
    {
    return _DefaultDataSource;
    }
    }

    Luke Breuer wrote:

    I envision abstract static members working the same way, except that they may only be invoked on the derived type.

    How would you know at run-time which derived type to use to invoke the static member I can see where you are trying to go with this, but it conflicts with the concept of static members. Its not that they couldnt have created inheritable static members, its that it would break the logical definition of static, which is a type-based, instanceless concept. As I said in a previous post, this is mutually exclusive with inheritance and polymorphism, which are object-based, instance concepts.

    What it comes down to is that you just dont need static inheritance. There might be a very slight benefit to it, but that benefit is not worth destroying the logical separation between static and instance concepts. Make your members instance, and allow them to participate in your inheritance hierarchy, and your system will work fine.



  • Quintesv

    CommonGenius.com wrote:

    You dont have to create an instance to get the default values; you just have to have two different versions of the member, one static and one instance, as I demonstrated above.

    What if I want to get the default value outside of the class What if I want to have a method that takes the derived class' type and does some schema validation (checking that the attribute data matches the column schema in the database); does that validation class need to create an instance of the type to get at the default value

    CommonGenius.com wrote:
    Because without an instance of an object, how do you know which derived class to look for
    You seem to be missing what I tried to describe in my previous posts. If Bar derives from Foo, and Foo has a static abstract member Yay, then invoking Foo.Yay is a syntax error. However, Bar.Yay is perfectly acceptable. Within Foo, one is allowed to access Yay from a static method, but any code that touches said static method must be invoked in Bar. In other words, whenever Yay is invoked in Foo, the first non-Foo member on the stack must be a Bar member. It might get a little tricky to implement this in the compiler, but I don't see why it is absolutely a bad idea and in violation of inheritance. This just seems to be a different type of inheritance, static-based inheritance. Is it necessarily a bad idea


  • St3veM

    How could you reference DefaultDataSource without a type How would the compiler know which DefaultDataSource you were referring to The only way it would know would be to determine at run-time the actual type of the instance you are referring to; but if the instance is what determines which property to call, then there is no reason not to make the DefaultDataSource an instance property. It would still allow you to put your code in the base class; what is preventing you from doing that

  • Bissy

    One is allowed to access static members without their type when inside said type.

    DefaultDataSource should most definitely be static -- it's the default for all instances of the class. I have the static method SelectSql which relies on DefaultDataSource; this SelectSql method should most definitely be static, as it's not instance-specific. While it doesn't make sense to put SelectSql in a base class, it does make sense to put DeleteSql in the derived class, as it only relies on DefaultDataSource. However, this is not possible, because one cannot override static members.

    I know just the slightest bit amount vtables and how they relate to inheritance, so let me know if I really need to dig into that to see why what I want isn't possible.

    When an abstract method is invoked on a instance typed as the base class, the CLR knows to actually call the derived instance's method. When said method is invoked on the derived instance, the same method gets called, perhaps without some indirection.

    I envision abstract static members working the same way, except that they may only be invoked on the derived type. This way, the CLR knows that when it hits an unnamed invokation of a static abstract member in the base class, it actually has to go to the derived class's definition. Here lies the rub: the pointers to said members would need an additional level of indirection, which could be tricky.

    Am I on the right track

  • Marcel Meijer

    Again, even if the compiler did enforce this, what purpose would it serve

  • Waseem Aslam

    Static members and inheritance/polymorphism are two entirely separate concepts that do not logically interact. A static member by definition is an instance-less concept; it belongs to the type in which it is declared. Whereas inheritance and polymorphism by definition are instance concepts; the actual type of the instance being operated on is what determines the behavior. For the compiler to force a derived class to implement a static member would be to break this logical separation; just because it could be used in reflection is not a good enough reason to do this.

    If you have come across a situation where you feel like you need to force a derived class to implement a static method, there might be a flaw in your object hierarchy somewhere (again, because of the logical separation between the two concepts). In your case, there is no reason to make the DefaultDataSource property static; it should be an instance property, which would allow it to be properly used through inheritance.



  • Abhi Win

    that's true, I haven't though this way... thanks!

  • Abstract classes and static methods