Подтвердить что ты не робот

Должно ли выражение типа "динамическое" вести себя одинаково во время выполнения как нединамическое одно из того же времени выполнения?

Рассмотрим следующую примерную программу:

using System;
public delegate string MyDelegateType(int integer);

partial class Program
{
    static string MyMethod(int integer) { return integer.ToString(); }

    static void Main()
    {
        Func<int, string> func = MyMethod;

        // Scenario 1: works
        var newDelegate1 = new MyDelegateType(func);
        newDelegate1(47);

        // Scenario 2: doesn’t work
        dynamic dyn = func;
        var newDelegate2 = new MyDelegateType(dyn);
        newDelegate2(47);
    }
}

Первый работает как ожидалось - конверсия в MyDelegateType завершается успешно. Второй, однако, выдает сообщение RuntimeBinderException с сообщением об ошибке:

Невозможно неявно преобразовать тип 'System.Func < int, string > ' to 'MyDelegateType'

Есть ли что-либо в спецификации С#, которая допускает это поведение, или это ошибка в компиляторе Microsoft С#?

4b9b3361

Ответ 1

Хорошая добыча Тимви.

Наша поддержка динамических групп методов слаба. Например, рассмотрим этот более простой случай:

class C
{
  public void M() {}
}

class P
{
    static void Main()
    {
        dynamic d = new C();
        C c = new C();
        Action a1 = c.M; // works
        Action a2 = d.M; // fails at runtime

d.M интерпретируется как свойство get (или доступ к полю) динамической исполняемой средой, и когда он разрешается как группа методов, он не работает во время выполнения.

То же самое происходит и в вашем случае, это просто немного неясно. Когда вы говорите MyDelegate x = new MyDelegate(someOtherDelegate);, который обрабатывается компилятором так же, как если бы вы сказали MyDelegate x = someOtherDelegate.Invoke;. Динамическая часть выполнения не знает, чтобы сделать это преобразование, и даже если бы это произошло, она не смогла обработать решение группы методов, которая является результатом части .Invoke выражения.

Есть ли что-либо в спецификации С#, которая допускает это поведение, или это ошибка в компиляторе Microsoft С#?

В спецификации не указано, что это должна быть ошибка времени выполнения, и подразумевает, что она должна корректно обрабатываться во время выполнения; ясно, что реализация этого не делает. Хотя это и является недостатком реализации, я бы не назвал это "ошибкой", потому что мы сознательно сделали поведение, которое вы обнаружили. У нас не было ресурсов, чтобы эти виды выражений работали правильно, поэтому мы оставили их в качестве ошибок. Если мы когда-либо получим хороший способ представления групп методов в динамической среде выполнения, мы можем ее реализовать.

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

Сэм рассказал об этом еще в 2008 году; см. его статью:

http://blogs.msdn.com/b/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx

Ответ 2

Я тоже воспользовался этим ограничением. Хотя я не мог ответить на вопрос, почему лучше, чем Эрик Липперт, есть простой способ обхода пути.

 var newDelegate2 = new MyDelegateType(x => dyn(x));

Он неявно получает статическую подпись от делегата, а динамический вызов работает без дополнительной информации. Это работает для делегатов и, в качестве бонуса, динамически вызываемых объектов.