使用索引器作为方法的参数签名/合同/类型

时间:2013-05-17 18:31:57

标签: c# indexer

作为一个例子,我将使用SqlDataReader和DataRow类:它们都定义了以下索引器:

public object this[int columnIndex] { get; set; }

用作方法参数类型的最小公分母类型是什么,因此两者(以及实现相同索引器的任何其他类)都可以以相同的方式传递和使用,例如:

void DoSomething(??? indexedObject)
{
   string foo = indexedObject[0].ToString(); 
          // let's ignore the presence of ToString()
          // for the purpose of this question
}

object吗?如果索引对象不是从object派生的(我认为这是可能的,即使非常不可能),该怎么办。

如果重要,我的目标是.NET 3.5。

编辑:我正在寻找一些合同执行,导致调用者传递实现所述索引器的对象。

2 个答案:

答案 0 :(得分:7)

  

是对象吗?

差不多。没有可以使用的通用接口或类,因此object实际上是唯一保证在层次结构中共享的东西。

  

如果索引对象不是从对象派生的,该怎么办?

这在.NET中是不可能的。 System.Object是所有类型的基类型,并且值始终可以被视为对象(即使它们需要装箱才能使其工作)。

但是,传递为object将不提供对索引器的访问,而不是通过反射。

唯一直接的方法是通过dynamic,但这需要.NET 4(并且不是类型安全的,这意味着你可以获得运行时异常)。

更好的方法可能是提供一个Func<T, int, string>,允许您在呼叫站点指定如何提取值:

void DoSomething<T>(T object, Func<T, int, string> valueExtractor)
{
    string foo = valueExtractor(object, 0); 
}

然后致电:

DoSomething(indexedObject, (o,i) => o[i].ToString());

这允许您传入一个对象和一种机制来提取给定调用站点索引的值,该值适用于任何类型。


编辑方面:

  

编辑:我正在寻找一些合同执行,导致调用者传递实现所述索引器的对象。

没有这些类型实现的内置契约或接口,也没有任何基于索引器存在来约束泛型的方法。您将需要一种不同的方法,例如我建议使用委托来提取值。

答案 1 :(得分:1)

我会说Reed Copsey的Func<T>委托解决方案是最好的方式,但是因为你在c#3.5上你必须去定义你自己的代表,Func<T>赢了不可用。这很简单。

编辑 - 哎呀,这是3.5而不是4.0。

对于它的价值,这是另一个解决方案,正如我在评论中所概述的,使用接口来定义适配器类型,以了解如何调用专用类型索引器但允许调用站点(DoSomething)使用通用界面:

void Main()
{
    var t1 = new Type1(); // ie Sql Reader
    var t2 = new Type2(); // ie DataRow

    DoSomething(new Type1IndexAdapter(t1));
    DoSomething(new Type2IndexAdapter(t2));
}

public void DoSomething(ICanIndex indexer)
{
    var r = indexer["test"];
}

public interface ICanIndex
{
    string this[string index]{get;}
}

public class Type1IndexAdapter : ICanIndex
{
    public Type1 value;
    public Type1IndexAdapter(Type1 val)
    {
        this.value = val;
    }
    public string this[string index]
    {
        get
        {
            return this.value[index];
        }
    }
}

public class Type2IndexAdapter : ICanIndex
{
    public Type2 value;
    public Type2IndexAdapter(Type2 val)
    {
        this.value = val;
    }
    public string this[string index]
    {
        get
        {
            return this.value[index];
        }
    }
}

public class Type1 // ie SqlDataReader 
{
    public string this[string index]
    {
        get
        {
            Console.WriteLine("Type 1 indexer called: " + index);
            return null;
        }
    }
}


public class Type2 // ie DataRow 
{
    public string this[string index]
    {
        get
        {
            Console.WriteLine("Type 2 indexer called: " + index);
            return null;
        }
    }
}