7

Я работаю с функцией, которая принимает две функции в качестве параметров, и возвращает новый сочинял один:Почему компилятор C# не может вывести делегат общего типа из сигнатуры функции?

public static Action<T> Compose<T>(Action<T> first, Action second) 
{ 
    return new Action<T>(arg => 
    { 
     first(arg); 
     second(); 
    }); 
} 

Я заметил, что компилятор жалуется, если я не указываю T, при отправке его в статический или член функции (в отличие от фактического объекта Action<T>):

static void Main(string[] args) 
{ 
    // compiler error here 
    var composed = Compose(Test,() => Console.WriteLine(" world")); 
    composed("hello"); 

    Console.ReadLine(); 
} 

public static void Test(string arg) 
{ 
    Console.Write(arg); 
} 

сообщение об ошибке:

The arguments for method 'ConsoleTest.Program.Compose(System.Action, System.Action)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Мой вопрос: Почему аргумент типа не может быть выведен здесь? Подпись Test известна во время компиляции, не так ли? Есть ли какая-то функция, которую вы могли бы поставить вместо Test, что может привести к неоднозначности ее подписи?

Сноска: Я знаю, что могу просто послать new Action<string>(Test) вместо Test к Compose (как отмечено в this question) - мой вопрос «почему», а не «как я могу это сделать».

+1

FYI - 'Compose ' исправляет это также. –

+3

Посмотрите этот ответ: http://stackoverflow.com/questions/6229131/why-cant-c-sharp-infer-type-from-this-seemingly-simple-obvious-case – lukegravitt

+0

Спасибо @lukegravitt - лучший ответ там ссылается на спецификацию языка, и сам Липперт перехватывает. – McGarnagle

ответ

6

Я полагаю, что это, вероятно, связано с тем, что, по крайней мере, с точки зрения компилятора, Test фактически является «группой методов», пока компилятор не определит, какие типы параметров он будет иметь. Это верно, даже если в группе есть только один метод (только один метод Test в текущей области).

Наблюдайте:

var composed = Compose<object>(Test,() => Console.WriteLine(" world")); 

дает ошибку:

The best overloaded method match for ' Compose<object>(System.Action<object>, System.Action) ' has some invalid arguments

Argument 1: cannot convert from 'method group' to ' System.Action<object> '

Но это нормально:

var composed = Compose<string>(Test,() => Console.WriteLine(" world")); 

Я думаю, что компилятор видеть выражение групповой метод как (Test) и неявно набрал общий вызов метода (Compose) как «unb ound "в некотором смысле. Он не может полностью определить, какой метод выбрать из группы методов из типа «несвязанной» сигнатуры параметра до Compose, и он не может определить, какой параметр типа типа для Compose из подписи. Для того, чтобы скомпилировать весь оператор, ему нужно «привязать» один или другой.

+2

Кажется, вы на правильном пути ... в этом разделе больше обсуждений, здесь: http://stackoverflow.com/a/6231921/1001985 – McGarnagle

0

Возможно, это связано с ковариацией. Хотя тип аргумента Test известен, вам может понадобиться создать делегат более определенного типа.

public class BaseClass { } 
public class DerivedClass : BaseClass { } 

static class Program 
{ 
    static void Main(string[] args) 
    { 
     var composed = Compose<DerivedClass>(Test,() => Console.WriteLine(" world")); 
     composed(new DerivedClass()); 

     Console.ReadLine(); 
    } 

    public static void Test(BaseClass arg) 
    { 
     Console.Write(arg); 
    } 
} 
Смежные вопросы