Наследование статических членов в C # - PullRequest
6 голосов
/ 01 февраля 2010

У меня есть несколько классов, которые отражают таблицы в базе данных. Я хотел бы иметь базовый класс, который имеет некоторую базовую функциональность (скажем, у него будет флаг «isDirty»), и статический массив строк с именами столбцов, которые появляются в базе данных. Следующий код не работает, но иллюстрирует, что я хотел бы сделать:

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])
    }
  }
}

Мне бы хотелось, чтобы у каждого производного класса был свой статический массив строк с именами столбцов базы данных, и я бы хотел, чтобы универсальный класс обращался к этому статическому члену без необходимости в экземпляре.

Но это не работает:
(A) columnNames - это идентичный массив в BaseRec, PeopleRec и OrderRec. Я не могу иметь столбцы Имена будут другими. BaseRec.columnNames.Length будет равно 3, потому что columnNames в OrderRec инициализируется последним.
(B) Обозначение T.columnNames не компилируется.

Есть идеи, как это исправить?

Ответы [ 4 ]

3 голосов
/ 01 февраля 2010

Проблема в том, что вы хотите связать некоторые данные с типами, а не с экземплярами типов. Я не уверен, что в C # есть изящный способ сделать это, но одна возможность - использовать статический Dictionary<Type, string[]> на BaseRecord. Ниже приведен пример, вы можете это исправить, добавив несколько общих статических членов в BaseRecord для инициализации / доступа к именам записей (и добавив некоторые проверки ошибок ...):

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);
        }
    }
}
1 голос
/ 01 февраля 2010

Массивы являются ссылочными типами. Просто включите нестатическое свойство в базу и внедрите его в свои производные классы, создав статический член для всех экземпляров, на которые будут ссылаться. Накладные расходы действительно довольно малы. Я обещаю, вы не заметите.

О, и ReadOnlyCollection<string> будет лучше, чем массив для этих имен. Примерно так:

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

или

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;    
}

или с C # 4 ( при условии, что мое понимание динамики верное ):

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;    
}

Правда, я ставлю под сомнение преимущества этого класса. Интерфейс, который он предоставляет, дает только нетипизированный доступ к фактическим данным, и это не очень полезно. Вы можете попытаться исправить это с помощью дженериков, но затем вы получите что-то вроде этого:

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

где каждый "T" - это совершенно другой класс с полями из одной таблицы. Он не дает вам хорошего синтаксиса, и к тому времени, когда вы реализуете эти другие классы, вы также можете напрямую связать имена столбцов и член isDirty.

Мне кажется, что лучшее решение - это когда я ранее упомянул "интерфейс". То, что вы действительно хотите, выглядит примерно так:

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

или возможно :

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

    dynamic this[int index]
    {
        get;
        set;
    }
}
1 голос
/ 01 февраля 2010

Почему бы вам просто не создать отдельные классы "глобальных имен", членами которых являются просто статические строки констант, содержащие текст, к которому вам нужен доступ.

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";
}

Редактировать:

Еще лучше, сделайте каждый статический класс глобальных имен внутренними классами описываемых ими классов, чтобы вместо OrderRecNames.OrderId вы могли набрать OrderRec.Names.OrderId, поэтому цель этого класса очевидна. Надеюсь, это поможет!

Редактировать 2: (чтобы быть очень конкретным)

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";
    }
}
0 голосов
/ 01 февраля 2010

Если бы у вас была статическая переменная, я бы сделал это:

  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();
  }

В противном случае я бы изменил их на общедоступные свойства (не статические) и просто добавил бы их в созданный массив производных классов (удалите статический вызов с помощью вызова свойства).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...