11

考虑这段代码:

  static void Main(string[] args)
  {
      var ints=new List<int> {10,11,22};
      Something(ints);//Output:Count is:3
      Something(new int[10]); //'System.Array' does not contain
                              //  a definition for 'Count'
      Console.ReadLine();     
  }
  static void Something(ICollection collection)
  {
      dynamic dynamic = collection;
      Console.WriteLine("Count is:{0}", dynamic.Count);
  }

当通过列表时一切正常。但是当传递数组并转换为动态时,我得到了这个错误:'System.Array' does not contain a definition for 'Count'

我知道我的解决方案是什么,但我想知道为什么编译器有这种行为?

4

4 回答 4

17
Something(new int[10]);

static void Something(ICollection collection)
{
    //The dynamic keyword tells the compilier to look at the underlying information
    //at runtime to determine the variable's type.  In this case, the underlying 
    //information suggests that dynamic should be an array because the variable you
    //passed in is an array.  Then it'll try to call Array.Count.
    dynamic dynamic = collection;
    Console.WriteLine("Count is:{0}", dynamic.Count);

    //If you check the type of variable, you'll see that it is an ICollection because
    //that's what type this function expected.  Then this code will try to call 
    //ICollection.Count
    var variable = collection;
    Console.WriteLine("Count is:{0}", variable.Count);
}

现在我们可以理解为什么 dynamic.Count 试图调用 System.Array.Count。但是,仍然不清楚为什么 Array.Count 在 Array 实现具有 Count 方法的 System.Collections.ICollection 时未定义。Array 实际上确实正确地实现了 ICollection,并且它确实有一个 Count 方法。但是,如果未将 Array 显式转换为 ICollection,则 Array.Count 的使用者无权访问 Count 属性。Array.Count 是用一种称为显式接口实现的模式实现的,其中 Array.Count 是为 ICollection 显式实现的。您只能通过使用此模式将变量转换为 ICollection 来访问 count 方法。这反映在Array 的文档中. 查找“显式接口实现”部分。

var myArray = new int[10];
//Won't work because Array.Count is implemented with explicit interface implementation
//where the interface is ICollection
Console.Write(myArray.Count);
//Will work because we've casted the Array to an ICollection
Console.Write(((ICollection)myArray).Count);
于 2013-10-20T05:26:40.393 回答
3

动态使用反射在内部工作。数组类没有属性 Count。它有一个属性 Length,它显式地实现了 ICollection 属性 Count。这意味着当您尝试进行动态调用时,它会失败,因为它找不到匹配的属性。

我对您的问题是,为什么在这种情况下您要尝试使用动态-您已经将其限制为支持接口的类,此时您应该使用该接口(这将起作用)。在这一点上,你几乎可以保证能够得到一个枚举器和计数——没有别的了。如果您需要更多,请考虑使用更好的界面。

于 2013-10-20T05:39:17.917 回答
0

“我知道我的解决方案是什么,但我想知道为什么编译器有这种行为?”

对于您的问题,我知道编译器的行为是.. 编译器在编译时不处理动态类型变量,因为动态类型是在运行时处理的。这就是出现该错误的原因。

如果您希望编译器处理这种情况,您可以将您的动态类型更改为var类型。

简而言之..动态类型变量不是编译器的责任。

于 2013-10-20T05:13:52.163 回答
0

这是因为该属性的名称不是 Count,而是System.Collections.ICollection.get_Count

如果你跑

foreach (var item in typeof(int[]).GetMembers(BindingFlags.NonPublic |
                                              BindingFlags.Public    |
                                              BindingFlags.Instance))
    Console.WriteLine(item.Name);

你会回来的

...
System.Collections.ICollection.get_Count
...

这是因为接口是显式实现的。

于 2013-10-20T06:09:28.160 回答