【问题标题】:Static member inheritance in C#C#中的静态成员继承
【发布时间】:2010-02-01 04:13:15
【问题描述】:

我有许多反映数据库中表的类。我想要一个具有一些基本功能的基类(例如,它会有一个“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.Length 将为 3,因为 OrderRec 中的 columnNames 是最后初始化的。
(B) 符号 T.columnNames 无法编译。

关于如何解决这个问题的任何想法?

【问题讨论】:

    标签: c# inheritance static


    【解决方案1】:

    问题是您希望将一些数据与类型相关联,而不是与类型的实例相关联。我不确定在 C# 中是否有一种简洁的方法,但一种可能性是在 BaseRecord 上使用静态 Dictionary&lt;Type, string[]&gt;。下面是一个示例,您可以通过在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);
            }
        }
    }
    

    【讨论】:

    • 谢谢戴夫。我考虑过使用字典作为最后的手段(仅仅是因为额外的性能开销很小),但我没有考虑使用 typeof(T) 作为关键字段。您的解决方案当然很优雅。
    • 非常感谢。我的解决方案在类上使用属性,但如果你需要反复点击它(因为反射),它会非常缓慢......如果我最终需要这个,我可能会切换到这个。
    【解决方案2】:

    您为什么不创建单独的“全局名称”类,其成员只是包含您需要访问的文本的静态常量字符串。

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

    编辑:

    更好的是,将全局名称的每个静态类都设置为它们所描述的类的内部类,这样您就可以键入OrderRec.Names.OrderId,而不是OrderRecNames.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";
        }
    }
    

    【讨论】:

    • 因为我想要一个泛型类来处理任意数量的 BaseRecord 子级,所以我需要一个数组或列表或某些字段,而不是命名字段(如,我不能名为 FirstName、LastName 等的字段)。另外,我可以在通用类中访问像 T.Columns.OrderID 这样的“列”类吗?
    【解决方案3】:

    数组是引用类型。只需在基类中包含一个非静态属性,并通过为所有要引用的实例创建一个静态成员来在派生类中实现它。开销真的很小。我保证你不会注意到的。

    哦,对于这些名称,ReadOnlyCollection&lt;string&gt; 会比数组更好。像这样的:

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

    不过,我真的怀疑这门课的好处。它提供的界面只为您提供对实际数据的非类型化访问,这不是很有用。您可能会尝试使用泛型来修复它,但最终会得到这样的结果:

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

    【讨论】:

    • 感谢您的回答。但是,我担心所需的存储空间会增加一倍。如果每个 IRecord 或 BaseRecord 或任何东西都有它的数据和 columnNames 数组,它的大小会加倍(或更多)。我的应用程序的内存已经达到极限了。
    • @Marc - 您错过了重要的一点:我建议您为每个 IRecord 保留一份数组的副本!数组是引用类型。我建议您为您的 IRecord 类型构建一个单独的静态集合,并使用对该静态集合的引用来实现您的 columnNames 属性。每个实例只有 16 个字节。我保证你的应用可以处理。
    • 乔尔:我确实没有抓住重点。您是说创建列名字符串并将其存储在某处,并让每个 IRecord 实例指向(参考)该数组。当我第一次阅读它时,我错过了它。实际上,不会错过每个实例 16 个字节。谢谢!
    【解决方案4】:

    如果您必须拥有静态变量,我会这样做:

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

    否则,我会将它们更改为公共属性(非静态),然后将它们添加到创建的派生类数组中(使用属性调用删除静态调用)。

    【讨论】:

    • 这根本不起作用 - 列名的静态数组存在于基类中,并且在任何子类被实例化时将被覆盖(创建一个 PeopleRec,然后是一个 OrderRec,并检查每个已创建实例的静态属性)。此外,您可能不应该在构造函数中设置这些名称的逻辑,因为 99.9% 的时间,列名在执行期间不会更改。
    猜你喜欢
    • 2010-11-10
    • 1970-01-01
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多