Oliviers Antwort ist grundsätzlich korrekt, könnte aber zusätzliche Erklärungen enthalten.
Warum gibt die Angabe des Rückgabetyps für die statische Factory-Methode den konkreten Typ an, während der Aufruf des Konstruktors inline die Schnittstelle gibt? Ich würde sie beide erwarten die gleiche sein
Ihre Programmklasse auf die folgende Klasse entspricht:
class Program
{
static void Test(ThingCreator creator)
{
Console.WriteLine(creator.Method.ReturnType);
}
static IThing Anon1()
{
return new Foo();
}
static IThing Anon2()
{
return new Bar();
}
static void Main()
{
Test(new ThingCreator(Foo.Create));
Test(new ThingCreator(Bar.Create));
Test(new ThingCreator(Program.Anon1));
Test(new ThingCreator(Program.Anon2));
}
}
Und jetzt sollte klar sein, warum das Programm druckt, was es tut.
Die Moral der Geschichte ist hier, dass, wenn wir die verborgene Methode für die Lambda-Ausdrücke erzeugen, diese verborgenen Methoden zurückgeben, was von den Delegierten erforderlich war, und nicht unabhängig von der Lambda- zurückgibt.
Warum ist das?
Ein Germane Beispiel würde zeigen, dass:
static void Blah(Func<object> f)
{
Console.WriteLine(f().ToString());
}
static void Main()
{
Blah(() => 123);
}
Ich hoffe, Sie stimmen zu, dass dies als
static object Anon() { return (object)123; }
und nicht
static int Anon() { return 123; }
erzeugt werden muss, weil dieser nicht sein kann konvertiert in Func<object>
! Es gibt keinen Platz für den Boxbefehl, aber der Aufruf an ToString
erwartet, dass ein Referenztyp von f()
zurückgegeben wird.
Die allgemeine Regel lautet also, dass das Lambda, wenn es als versteckte Methode verdinglicht wird, den Rückgabetyp haben muss, der durch die Konvertierung in den Delegattyp gewährt wird.
Gibt es auch eine Möglichkeit, in der Lambda-Version anzugeben, dass der Rückgabewert der konkrete Typ sein soll? Oder ruft eine statische Methode den einzigen Weg auf?
Sicher.
interface IThing {}
class Foo : IThing {}
delegate T ThingCreator<T>() where T : IThing;
public class Program
{
static void Test<T>(ThingCreator<T> tc) where T : IThing
{
Console.WriteLine(tc.Method.ReturnType);
}
public static void Main()
{
Test(() => new Foo());
}
}
Warum ist das anders?
Da Inferenz Typ folgert, dass T
Foo
ist und deshalb konvertieren wir die Lambda ThingCreator<Foo>
, welcher Typ Foo
zurückkehren muss. Daher gibt die generierte Methode Foo
zurück, wie der Delegattyp erwartet.
Aber warten Sie, sagen Sie ... ich nicht die Unterschrift des Tests oder von ThingCreator
keine Sorgen ändern können! Sie können immer noch diese Arbeit machen:
delegate T ThingCreator<T>() where T : IThing;
delegate IThing ThingCreator();
public class Program
{
static void Test(ThingCreator tc)
{
Console.WriteLine(tc.Method.ReturnType);
}
static ThingCreator DoIt<T>(ThingCreator<T> tc) where T : class, IThing
{
return tc.Invoke;
}
public static void Main()
{
Test(DoIt(() => new Foo()));
}
}
Die Kehrseite ist, dass jetzt jeder Ihrer Nicht-generic ThingCreator
Delegierten ein Delegierter ist, die einen ThingCreator<T>
Delegierten aufruft, die ein bisschen eine Verschwendung von Zeit und Speicher ist. Aber Sie erhalten Typinferenz, Methodengruppenumwandlung und Lambda-Konvertierung, um Sie zu einer Methode des gewünschten Rückgabetyps und zu einem Delegaten für diese Methode zu machen.
Beachten Sie die class
Einschränkung. Sehen Sie, warum diese Einschränkung da sein muss? Dies ist eine Übung für den Leser.
Eine __static__ Methode ist nicht der einzige Weg. Sie können auch eine nicht statische verwenden. Zum Beispiel 'Func f =() => neu Foo(); Test (f.Invoke); 'Aber die wirkliche Frage ist, warum checkst du' .Method.ReturnType' auf einer (möglicherweise Multicast) Delegierteninstanz? Was nützt das? Warum kümmert dich das? –
@Jeppe - Eine Bibliothek eines Drittanbieters verfügt über eine öffentliche API mit einer ähnlichen Methode wie "Test". Ich habe den Nebeneffekt nicht erwartet, indem ich ein Lambda anstelle eines Verweises auf eine tatsächliche Funktion übergeben habe. Musste graben um zu finden, dass sie den Rückgabetyp der Methode überprüften. Wenn es mein eigener Code wäre, würde ich das nicht schaffen. –