Marc Meketon
Marc Meketon

Reputation: 2695

Static member inheritance in C#

I have a number of classes that reflect tables in a database. I would like to have a base class that has some basic functionality (say, it would have a "isDirty" flag), and a static array of strings with the column names as they appear in the database. The following code doesn't work but illustrates what I would like to do:

public class BaseRecord {  
  public bool isDirty;  
  public object [] itemArray;  
  public static string [] columnNames;    
}

public class PeopleRec : BaseRecord {  
}

public class OrderRec : BaseRecord {  
}

public static void Main() {
  PeopleRec.columnNames = new string[2];
  PeopleRec.columnNames[0]="FIRST_NAME";
  PeopleRec.columnNames[1]="LAST_NAME";

  OrderRec.columnNames = new string[4];
  OrderRec.columnNames[0] = "ORDER_ID";
  OrderRec.columnNames[1] = "LINE";
  OrderRec.columnNames[2] = "PART_NO";
  OrderRec.columnNames[3] = "QTY";
}

public class DoWork<T> where T : BaseRecord {
  public void DisplayColumnNames() {
    foreach(string s in T.columnNames)
      Console.Write("{0}", s);
    }
  public void DisplayItem(T t) {
    for (int i=0; i<itemValues.Length; i++) {
      Console.Write("{0}: {1}",t.columnNames[i],t.itemValues[i])
    }
  }
}

I would like each derived class to have it's own static array of strings of database column names, and I would like the generic class to access this static member without the need for an instance.

But it doesn't work:
(A) columnNames is the identical array in BaseRec, PeopleRec and OrderRec. I cannot have columnNames be different. BaseRec.columnNames.Length would be 3 because the columnNames in OrderRec is initialized last.
(B) The notation T.columnNames does not compile.

Any ideas on how to fix this?

Upvotes: 6

Views: 3464

Answers (4)

SwDevMan81
SwDevMan81

Reputation: 50028

If you had to have the static variable I would do this:

  public class BaseRecord
  {
     public bool isDirty;
     public object[] itemArray;
     public static string[] columnNames;

     public void DisplayColumnNames()
     {
        foreach (string s in columnNames)
           Console.Write("{0}\n", s);
     }

     public void DisplayItem()
     {
        for (int i = 0; i < itemArray.Length; i++)
        {
           Console.Write("{0}: {1}\n", columnNames[i], itemArray[i]);
        }
     }
  }

  public class PeopleRec : BaseRecord
  {
     public PeopleRec()
     {
     }
  }

  public class OrderRec : BaseRecord
  {
     public OrderRec()
     {
     }
  }

  public static void Main()
  {
     PeopleRec.columnNames = new string[2];
     PeopleRec.columnNames[0] = "FIRST_NAME";
     PeopleRec.columnNames[1] = "LAST_NAME";

     OrderRec.columnNames = new string[4];
     OrderRec.columnNames[0] = "ORDER_ID";
     OrderRec.columnNames[1] = "LINE";
     OrderRec.columnNames[2] = "PART_NO";
     OrderRec.columnNames[3] = "QTY";

     BaseRecord p = new PeopleRec();
     p.DisplayColumnNames();
     p.itemArray = new object[2];
     p.itemArray[0] = "James";
     p.itemArray[1] = "Smith";
     p.DisplayItem();

     BaseRecord o = new OrderRec();
     o.DisplayColumnNames();
     o.itemArray = new object[4];
     o.itemArray[0] = 1234;
     o.itemArray[1] = 1;
     o.itemArray[2] = 39874;
     o.itemArray[3] = 70;
     o.DisplayItem();
  }

Otherwise, I would change them to public properties (none static) and just add them to the created derived class array (remove the static call with a property call).

Upvotes: -1

Joel Coehoorn
Joel Coehoorn

Reputation: 416131

Arrays are reference types. Just include a non-static property in the base, and implement it in your derived classes by creating a static member for all the instances to refer to. The overhead is really pretty small. I promise you won't notice.

Oh, and a ReadOnlyCollection<string> would be better than an array for those names. Something like this:

public class BaseRecord {  
  public bool isDirty;  
  public IEnumerable<Object> items;  
  public ReadOnlyCollection<string> columnNames;    
}

or

public class BaseRecord {  
  public bool isDirty;  
  public IList<Object> items;  

  public Object this[int index]
  {
     get { return items[index];}
     set { items[index] = value;}
  }

  public ReadOnlyCollection<string> columnNames;    
}

or with C# 4 (assuming my understanding of dynamic is correct):

public class BaseRecord {  
  public bool isDirty;  
  public IList<dynamic> items;  

  public dynamic this[int index]
  {
     get { return items[index];}
     set { items[index] = value;}
  }

  public ReadOnlyCollection<string> columnNames;    
}

Really, though, I question the benefits of this class. The interface it provides only gives you un-typed access to the actual data, and that isn't very useful. You might try to fix it with generics, but then you end up with something like this:

public Record<T>
{
    public bool isDirty;
    public ReadOnlyCollection<string> columnNames;
    public T data;
}

where each "T" is a completely different class with the fields from a single table. It doesn't give you good syntax, and by the time you implement those other classes you may just as well tack the column names and isDirty member in directly.

It seems to me that a better solution is when I previously mentioned an "interface". What you really want looks something more like this:

public IRecord
{
    bool isDirty;
    ReadOnlyCollection<string> columnNames;
}

or maybe:

public IRecord
{
    bool isDirty;
    ReadOnlyCollection<string> columnNames;

    dynamic this[int index]
    {
        get;
        set;
    }
}

Upvotes: 1

Dave
Dave

Reputation: 4468

The issue is you that you want to associate some data with the types, not with instances of the types. I'm not sure that there's a neat way of doing this in C#, but one possibility is using a static Dictionary<Type, string[]> on BaseRecord. An example is below, you could neaten this up by adding some generic static members on BaseRecord for initializing/accessing the record names (and add some error checking...):

using System;
using System.Collections.Generic;

namespace Records
{
    public class BaseRecord
    {
        public bool isDirty;
        public object[] itemArray;

        public static Dictionary<Type, string[]> columnNames = new Dictionary<Type, string[]>();
    }

    public class PeopleRec : BaseRecord
    {
        static PeopleRec()
        {
            string[] names = new string[2];
            names[0] = "FIRST_NAME";
            names[1] = "LAST_NAME";
            BaseRecord.columnNames[typeof(PeopleRec)] = names;
        }
    }

    public class DoWork<T> where T : BaseRecord
    {
        public void DisplayColumnNames()
        {
            foreach (string s in BaseRecord.columnNames[typeof(T)])
                Console.WriteLine("{0}", s);
        }

        public void DisplayItem(T t)
        {
            for (int i = 0; i < t.itemArray.Length; i++)
            {
                Console.WriteLine("{0}: {1}", BaseRecord.columnNames[typeof(T)][i], t.itemArray[i]);
            }
        }
    }

    class Program
    {
        public static void Main()
        {
            PeopleRec p = new PeopleRec
            {
                itemArray = new object[] { "Joe", "Random" }
            };

            DoWork<PeopleRec> w = new DoWork<PeopleRec>();
            w.DisplayColumnNames();
            w.DisplayItem(p);
        }
    }
}

Upvotes: 3

Mark Carpenter
Mark Carpenter

Reputation: 17775

Why don't you just create separate classes of "Global Names," whose members are simply static constant strings containing the text you need to access.

public static class OrderRecNames{
    public static const string OrderId =  "ORDER_ID";
    public static const string Line = "LINE";
    public static const string PartNo = "PART_NO";
    public static const string Qty = "QTY";
}

Edit:

Better yet, make each static class of Global Names inner classes of the classes they describe, so instead of OrderRecNames.OrderId, you could type OrderRec.Names.OrderId, so the class's purpose is very obvious. Hope that helps!

Edit 2: (to be very specific)

public class BaseRecord {  
    public bool isDirty;  
    public object [] itemArray;  
}

public class PeopleRec : BaseRecord {  
    class Columns {
        public static const string FirstName =  "FIRST_NAME";
        public static const string LastName = "LAST_NAME";
    }
}

public class OrderRec : BaseRecord {  
    class Columns {
        public static const string OrderId =  "ORDER_ID";
        public static const string Line = "LINE";
        public static const string PartNo = "PART_NO";
        public static const string Qty = "QTY";
    }
}

Upvotes: 1

Related Questions