2013-09-25 10 views
7

pracuję z funkcji, które ma dwie funkcje jako parametry i zwraca nowy opanowany jeden:Dlaczego kompilator C# nie może wnioskować o delegacie typu rodzajowego z podpisu funkcji?

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

Zauważyłem, że kompilator narzeka jeśli nie podasz T, wysyłając go do statyczne lub element funkcji (w przeciwieństwie do obiektu rzeczywistego 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); 
} 

komunikatu:

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

Moje pytanie: Dlaczego nie można wywnioskować argumentu typu? Sygnatura Test jest znana w czasie kompilacji, nieprawdaż? Czy jest naprawdę jakaś funkcja, którą można umieścić na miejscu Test, co spowoduje, że jej podpis będzie niejednoznaczny?

Przypis: Wiem, że mogę po prostu wysłać new Action<string>(Test) zamiast Test do Compose (jak wspomniano w this question) - moje pytanie „dlaczego”, a nie „jak mogę to zrobić”.

+1

FYI - "Komponuj ' naprawia to również. –

+3

Zobacz tę odpowiedź: http://stackoverflow.com/questions/6229131/why-cant-c-sharp-infer-type- from-this-seemingly-prosty-ow-case-case – lukegravitt

+0

Dzięki @lukegravitt - najlepsza odpowiedź odwołuje się do specyfikacji językowej, a sam Lippert kurczy się. – McGarnagle

Odpowiedz

6

Przypuszczam, że prawdopodobnie ma to coś wspólnego z faktem, że przynajmniej z punktu widzenia kompilatora Test jest w rzeczywistości "grupą metod", dopóki kompilator nie określi, jakie typy parametrów będzie miał. Jest to prawdą, nawet jeśli w grupie jest tylko jedna metoda (tylko jedna metoda Test w bieżącym zakresie).

przestrzegać:

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

daje błąd:

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

Ale to jest w porządku:

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

Domyślam się, że kompilator zobaczyć zarówno wyrażenie grupy metoda (Test) i niejawnie wpisane wywołanie metody ogólnej (Compose) jako "unb "w pewnym sensie. Nie można w pełni określić, która metoda wybierać z grupy metod z typu "niezwiązany" podpis parametru do Compose i nie może określić, który typ typu parametru dla Compose z podpisu. Aby "skompilować" całe stwierdzenie, potrzebuje jednego lub drugiego.

+2

Wygląda na to, że jesteś na dobrej drodze ... więcej dyskusji na ten temat, tutaj: http://stackoverflow.com/a/6231921/1001985 – McGarnagle

0

Może to mieć związek z kowariancją. Chociaż znany jest typ argumentu o nazwie Test, można utworzyć delegat bardziej konkretnego typu.

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); 
    } 
} 
Powiązane problemy