Są dwie rzeczy, które musisz wiedzieć, aby zrozumieć to zachowanie.
- Wszyscy delegaci pochodzą
System.Delegate
, ale różni delegaci mają różne typy i dlatego nie można ich przypisywać.
- Język C # zapewnia specjalną obsługę przypisywania metody lub lambda do delegata .
Ponieważ różni delegaci mają różne typy, oznacza to, że nie można przypisać delegata jednego typu do drugiego.
Na przykład biorąc pod uwagę:
delegate void test1(int i);
delegate void test2(int i);
Następnie:
test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a; // Using normal assignment, therefore does not compile.
Pierwszy wiersz powyżej kompiluje OK, ponieważ używa specjalnej obsługi do przypisywania lambda lub metody do delegata.
W rzeczywistości kompilator skutecznie przepisał następującą linię:
test1 a = new test1(Console.WriteLine);
Drugi wiersz powyżej nie kompiluje się, ponieważ próbuje przypisać wystąpienie jednego typu do innego niezgodnego typu.
Jeśli chodzi o typy, nie ma kompatybilnego przypisania pomiędzy test1
i test2
ponieważ są to różne typy.
Jeśli warto o tym pomyśleć, rozważ następującą hierarchię klas:
class Base
{
}
class Test1 : Base
{
}
class Test2 : Base
{
}
Poniższy kod nie będzie kompilować, choć Test1
i Test2
wywodzą się z tej samej klasy bazowej:
Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.
To wyjaśnia, dlaczego nie można przypisać jednego typu delegata do innego. To tylko normalny język C #.
Najważniejsze jest jednak zrozumienie, dlaczego możesz przypisać metodę lub lambda kompatybilnemu delegatowi. Jak wspomniano powyżej, jest to część obsługi języka C # dla delegatów.
Więc w końcu odpowiedz na twoje pytanie:
Gdy używasz Invoke()
, przypisujesz wywołanie METHOD do delegata, korzystając ze specjalnej obsługi języka C # do przypisywania metod lub lambd do delegata zamiast próbować przypisać niezgodny typ - stąd kompilacja jest OK.
Aby być całkowicie jasnym, kod, który kompiluje się w twoim OP:
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
Jest konwertowany koncepcyjnie na coś takiego:
public test Success()
{
Func<int, int> f = x => x;
return new test(f.Invoke);
}
Podczas gdy uszkodzony kod próbuje przypisać dwa niezgodne typy:
public test Fail()
{
Func<int, int> f = x => x;
return f; // Attempting to assign one delegate type to another: Fails
}