6 Stimmen

Generics, Überlastauflösung und Delegierte (sorry, kann keinen besseren Titel finden)

Mögliches Duplikat:
Warum ist Func<T> zweideutig mit Func<IEnumerable<T>>?

Ich habe ein sehr merkwürdiges Problem bei der Auflösung von Überlastungen mit Generika festgestellt...

Ziehen Sie die folgenden Methoden in Betracht:

static void Foo<TSource>(TSource element, Func<TSource, int> selector)
{
    "int".Dump();
}

static void Foo<TSource>(TSource element, Func<TSource, double> selector)
{
    "double".Dump();
}

static T Identity<T>(T value)
{
    return value;
}

(C# 4, getestet in LINQPad)

Wenn ich versuche zu telefonieren Foo mit einem Lambda-Ausdruck als Selektor, funktioniert alles einwandfrei:

Foo(42, x => x); // prints "int"

Aber wenn ich die x => x con Identity kann sich der Compiler nicht zwischen den 2 Foo Überlastungen:

Foo(42, Identity);
// The call is ambiguous between the following methods or properties:
// 'UserQuery.Foo<int>(int, System.Func<int,int>)' and
// 'UserQuery.Foo<int>(int, System.Func<int,double>)'

Wie kann die zweite Überlast ein gültiger Kandidat sein? Die Typinferenz bestimmt korrekt, dass TSource es int , so dass die T Parameter für die Identity muss die Methode int auch, daher muss der Rückgabetyp int auch... Identity könnte ein Func<int,int> oder eine Func<double,double> , aber nicht ein Func<int,double> !

Und es kommt noch schlimmer! Selbst wenn ich alle Typparameter explizit angebe, erhalte ich immer noch denselben Fehler:

Foo<int>(42, Identity<int>); // The call is ambiguous...

Wie kann es hier irgendeine Zweideutigkeit geben? Soweit ich das beurteilen kann, gibt es keine Möglichkeit, dass die Überladung, die eine Func<int,double> kann ein Kandidat sein. Ich vermute, die Erklärung muss irgendwo in den Spezifikationen stehen, aber ich kann die entsprechende Stelle nicht finden... oder es könnte ein Fehler im Compiler sein, aber das ist wohl eher unwahrscheinlich.

Beachten Sie, dass es funktioniert, wenn ich den Delegaten explizit erstelle:

Foo(42, new Func<int, int>(Identity)); // prints "int"

Kann mir jemand erklären, was hier vor sich geht? Außerdem: Warum funktioniert es mit einem Lambda, aber nicht mit einer Methodengruppe?

3voto

LukeH Punkte 251752

Liegt es nicht einfach daran, dass der Rückgabetyp nicht Teil der Signatur der Methode ist?

Die Tatsache, dass die Identity<T> der Argumenttyp und der Rückgabetyp der Methode garantiert gleich sind, wird vom Compiler nicht berücksichtigt, wenn er versucht, zu entscheiden, welche Überladung von Foo<TSource> erforderlich ist. Wenn der Rückgabetyp nicht berücksichtigt wird, dann Identity<int> könnte auch konvertierbar sein in Func<int, int> , Func<int, double> o Func<int, anything> .

1voto

Lucero Punkte 57715

Ich denke, dass LukeH Recht hat. Um jedoch den zweiten Teil Ihrer Frage zu beantworten: Der Delegat des Lambdas wird bereits alle Typen ausgefüllt haben (z.B. immer ein Func<int, int> si TSource es un int ), weshalb es in diesem Fall keine Zweideutigkeit gibt. Es ist nicht wie bei einer Funktionssignatur, wo der Rückgabetyp ignoriert wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X