Получить граф массива, который преобразован в динамический в С#

Рассмотрим этот код:

  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'.

Я знаю, что такое мое решение, но я хочу знать, почему у компилятора это поведение?

Ответ 1

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 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 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 реализует System.Collections.ICollection, который имеет метод Count. Массив действительно реализует ICollection правильно, и у него есть метод Count. Тем не менее, пользователи Array.Count не имеют права доступа к свойству Count без явного приведения массива в ICollection. Array.Count реализован с шаблоном, известным как явная реализация интерфейса, где Array.Count явно реализован для ICollection. И вы можете получить доступ только к методу count, переведя вашу переменную в ICollection с помощью этого шаблона. Это отражено в документах для 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);

Ответ 2

Динамические работы с использованием отражения. Класс массива не имеет свойства Count. Он имеет свойство Length, которое явно реализует свойство ICollection Count. Это означает, что когда вы пытаетесь выполнить динамический вызов, он терпит неудачу, потому что он не может найти подходящее свойство.

Мой вопрос в том, почему вы пытаетесь использовать динамику в этом случае - вы уже ограничили его классами, поддерживающими интерфейс, в этот момент вы должны использовать интерфейс (который будет работать). На этом этапе вы почти гарантированно сможете получить счетчик и счетчик - ничего больше. Если вам нужно больше, рассмотрите лучший интерфейс.

Ответ 3

"Я знаю, что такое мое решение, но я хочу знать, почему у компилятора это поведение?"

По вашему вопросу, поскольку я знаю, что поведение компилятора... Компилятор не обрабатывает переменную динамического типа во время компиляции, так как динамический тип обрабатывается во время выполнения. Вот почему эта ошибка появляется.

Если вы хотите, чтобы компилятор обрабатывал этот случай, вы можете изменить свой динамический тип на var.

в коротких словах.. переменная динамического типа не является ответственностью компилятора.

Ответ 4

Это потому, что имя свойства не 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
...

потому что интерфейс реализован явно.