У меня есть много классов, которые отражают таблицы в базе данных. Я хотел бы иметь базовый класс, который имеет некоторую основную функциональность (скажите, она имела бы флаг "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. У меня не может быть columnNames отличаться. BaseRec.columnNames. Длина была бы 3, потому что columnNames в OrderRec инициализируется в последний раз.
(B) Нотация T.columnNames не компилирует.
Какие-либо идеи о том, как зафиксировать это?
Проблема в том, что вы хотите связать некоторые данные с типами, а не с экземплярами типов. Я не уверен, что в C # есть изящный способ сделать это, но одна возможность - использовать статический Dictionary
в 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);
}
}
}
Почему бы Вам просто не создать отдельные классы "Global Names", членами которых являются просто статические константные строки, содержащие текст, к которому Вам необходимо получить доступ.
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";
}
Правка:
Лучше сделать каждый статический класс "Global Names" внутренними классами описанных ими классов, поэтому вместо 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";
}
}
Если вам нужно было сделать статическую переменную, я бы сделал это:
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();
}
в противном случае я бы изменил их в публичные свойства (не статично) и просто добавляю их в созданный производный классный массив ( Удалите статический вызов с вызовом свойств).
Массивы являются ссылочными типами. Просто включите нестатическое свойство в базу и реализуйте его в производных классах, создав статический член для всех экземпляров, на которые будут ссылаться. Накладные расходы действительно небольшие. Обещаю, ты не заметишь.
Да, и ReadOnlyCollection
было бы лучше, чем массив для этих имен. Примерно так:
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;
}
}