【问题标题】:Question about indexers and/or generics关于索引器和/或泛型的问题
【发布时间】:2010-10-15 19:50:29
【问题描述】:

如何知道一个对象是否实现了索引器?我需要为 DataRow 和 IDataReader 共享一个逻辑,但它们不共享任何接口。

我也尝试过使用泛型,但不知道应该对 where 子句施加什么限制。

public class Indexer {
    // myObject should be a DataRow or a IDataReader
    private object myObject;
    public object MyObject {
        get { return myObject; }
        set { myObject = value; }
    }
    // won't compile, myObject has no indexer
    public object this[int index] {
        get { return myObject[index]; }
        set { myObject[index] = value; }
    }
    public Indexer(object myObject) {
        this.myObject = myObject;
    }
}

public class Caller {
    void Call() {
        DataRow row = null;
        IDataReader reader = null;
        var ind1 = new Indexer(row);
        var ind2 = new Indexer(reader);
        var val1 = ind1[0];
        var val2 = ind1[0];
    }
}

【问题讨论】:

    标签: c# generics indexer


    【解决方案1】:

    您需要声明一个带有索引器属性的接口,将该接口用作约束,并且类型参数类需要实现该接口以满足约束。

    由于您无法控制要使用的类,因此这是行不通的。

    另一种方法是让Indexer 类将get/set 操作作为单独的参数:

    public class Indexer {
    
        private Func<int, object> getter;        
        private Action<int, object> setter;
    
        public object this[int index] 
        {
            get { return getter(index); }
            set { setter(index, value); }
        }
    
        public Indexer(Func<int, object> g, Action<int, object> s) 
        {
            getter = g;
            setter = s;
        }
    }
    
    public static class IndexerExtensions
    {
        public static Indexer ToIndexer(this DataRow row)
        {
            return new Indexer(n => row[n], (n, v) => row[n] = v);
        }
    
        public static Indexer ToIndexer(this IDataReader row)
        {
            return new Indexer(n => row[n], (n, v) => row[n] = v);
        }
    }
    

    然后你可以这样做:

    DataRow row = null;
    IDataReader reader = null;
    var ind1 = row.ToIndexer();
    var ind2 = reader.ToIndexer();
    var val1 = ind1[0];
    var val2 = ind1[0];
    

    【讨论】:

    • 但是我不能让 DataRow 实现我的接口
    • 它类似于 Reed 的,只是你不必声明一个类来处理每种情况——如果你有一次性的,你可以写几个 lambdas 来构造一个索引器,类似了解如何在 Java 中使用匿名类来创建一次性类。
    • 我想我会这样做,我会混合两个答案,tx
    • 请注意,IDataReader 没有设置器
    • 如果您想要完全的类型安全,您必须创建一个 ReadOnlyIndexer 来涵盖此类情况,这实际上可能就是您所需要的。或者一个 hacky 方法是传递一个什么都不做的 setter lambda!
    【解决方案2】:

    您可以使您的索引器成为一个抽象基类,具有两个子类,一个用于 DataRow,一个用于 IDataReader。

    为了更容易使用,可以存在 2 种工厂方法,例如:

    var ind1 = Indexer.CreateFromDataRow(row);
    var ind2 = Indexer.CreateFromIDataReader(reader);
    

    他们可以为该类型创建一个特定的基类,并使用它自己的逻辑来处理索引。

    这避免了为每个 get/set 调用不断检查类型的开销(以单个虚拟属性而不是标准属性为代价)。

    【讨论】:

      【解决方案3】:
      get { 
          DataRow row = myObject as DataRow;
          if (row != null)
              return row[index];
          IDataReader reader = myObject as IDataReader;
          if (reader != null)
              return reader[index];
      }
      

      并使用相同的设置逻辑{}

      【讨论】:

      • 我不想承担 Casting ["as" keyword] 的成本,所以答案不适合我,想象有一个读者有 200 条记录,它会是 400 次,我认为这一定会影响性能
      • 请注意,如果您实际上是在访问数据库以获取数据,则 400 次强制转换的成本可能不会很大。
      • 我同意 Earwicker 的观点。我只是更喜欢我的方法,因为它可以让您拥有更多的类型安全性 - 性能。用例可能完全可以接受,但我的方法允许您保存 IDataReader 而不是对象。
      猜你喜欢
      • 1970-01-01
      • 2011-08-01
      • 1970-01-01
      • 2011-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多