Почему методы с возвращаемым типом не могут соответствовать делегату с той же сигнатурой, который возвращает void?

Меня очень интересует это ограничение делегатов C#. Возьмите следующий код, который не компилируется:

class Program
{
   delegate void Foo(string s);

   static void Main(string[] args)
   {
      Foo f = Test; // Error
      f("Hello");
   }

   static int Test(string s)
   {
      return s.Length;
   }
}

Ошибка здесь в том, что делегат Foo не имеет возвращаемого типа, поэтому его нельзя использовать для создания ссылки на функцию Test.

Однако почему это проблема? Очевидно, что если бы кто-то вызывал Test через делегат f, он не имел бы доступа к возвращаемому значению Test, что совершенно справедливо, но мне кажется, что компилятор все равно мог бы генерировать type безопасный код, просто игнорируя возвращаемое значение, возможно, даже имея возможность оптимизировать этот случай.

Очевидно, что если бы ситуация была обратной, и Foo указал, что нужно вернуть string, а Test вернул void, у нас возникла бы проблема. Итак, я полностью согласен с тем, что этот случай должен приводить к ошибке компилятора. Однако почему нельзя int Test(string) неявно сопоставить делегата void Foo(string)?

Я ищу один из двух возможных ответов: во-первых, логическая проблема с предоставлением этой возможности. Есть ли случай, когда игнорирование типа возвращаемого значения при вызове метода через делегата может привести к небезопасному состоянию? Или, во-вторых, ссылка на спецификацию C#, в которой поясняется, почему эта реализация будет нарушать спецификацию.


person Mike Christensen    schedule 19.09.2013    source источник


Ответы (1)


Об этом говорится в спецификации C# (раздел 15.2 «Совместимость делегатов»).

Метод или делегат M совместим с типом делегата D, если выполняются все следующие условия:

...

· Существует тождество или неявное преобразование ссылки из возвращаемого типа M в возвращаемый тип D.

Такого преобразования между любым типом и пустотой не существует, поэтому это не должно быть разрешено в соответствии со спецификацией.

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

public Action Ignore<T>(Func<T> call)
{
    return () => call();
}
person Chris Hannon    schedule 20.09.2013