Ich habe Probleme, den Compiler die richtige Überladung für eine Erweiterungsmethode aufzulösen. Der beste Weg für mich zu erklären ist mit ein wenig Code. Hier ist ein LINQPad Skript, das das Problem veranschaulicht. Dies wird nicht kompiliert, da das Problem ich habe:Weird-Extension-Methode Überladungsauflösung
void Main(){
new Container<A>().Foo(a=>false);
}
interface IMarker{}
class A : IMarker{
public int AProp{get;set;}
}
class B : IMarker{
public int BProp{get;set;}
}
class Container<T>{}
static class Extensions{
public static void Foo<T>(this T t, Func<T, bool> func)
where T : IMarker{
string.Format("Foo({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Foo<T>(this Container<T> t, Func<T, bool> func){
string.Format("Foo(Container<{0}>)", typeof(T).Name).Dump();
}
}
irrtümlicher ich erhalte, ist:
Der Anruf zwischen den folgenden Methoden oder Eigenschaften nicht eindeutig ist: ‚
Extensions.Foo<Container<A>>(Container<A>, System.Func<Container<A>,bool>)
‘ und "Extensions.Foo<A>(Container<A>, System.Func<A,bool>)
'
Es scheint mir, dass es überhaupt nicht mehrdeutig ist. Die erste Methode akzeptiert keine Container<T>
, nur eine IMarker
. Es scheint, wie die allgemeinen Einschränkungen nicht in der Überladungsauflösung zu unterstützen, aber in dieser Version des Codes, haben sie zu sein scheinen:
void Main(){
new A().Bar();
new A().Foo(a=>a.AProp == 0);
new A().Foo(a=>false); // even this works
new A().Foo(a=>{
var x = a.AProp + 1;
return false;
});
new Container<A>().Bar();
new Container<A>().Foo(a=>a.AProp == 0);
new Container<A>().Foo(a=>{
var x = a.AProp + 1;
return false;
});
}
interface IMarker{}
class A : IMarker{
public int AProp{get;set;}
}
class B : IMarker{
public int BProp{get;set;}
}
class Container<T>{}
static class Extensions{
public static void Foo<T>(this T t, Func<T, bool> func)
where T : IMarker{
string.Format("Foo({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Foo<T>(this Container<T> t, Func<T, bool> func){
string.Format("Foo(Container<{0}>)", typeof(T).Name).Dump();
}
public static void Bar<T>(this T t) where T : IMarker{
string.Format("Bar({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Bar<T>(this Container<T> t){
string.Format("Bar(Container<{0}>)", typeof(T).Name).Dump();
}
}
Dies kompiliert und erzeugt die erwarteten Ergebnisse:
Bar (A: IMarker)
Foo (A: IMarker)
Foo (A: IMarker)
Foo (A: IMarker)
Bar (Container <A>)
Foo (Container <A>)
Foo (Container <A>)
Es scheint nur ein Problem zu haben, wenn ich nicht den Lambda-Parameter in der Lambda-Ausdruck verweisen und dann nur mit der Container<T>
Klasse. Beim Aufruf Bar
gibt es kein Lambda, und es funktioniert gut. Beim Aufruf von Foo
mit dem Rückgabewert basierend auf dem Lambda-Parameter funktioniert es gut. Auch wenn der Rückgabewert des Lambda derselbe ist wie der im Beispiel, der nicht kompiliert wird, aber der Lambda-Parameter durch eine Dummy-Zuweisung referenziert wird, funktioniert er.
Warum funktioniert es in diesen Fällen, aber nicht in der ersten? Mache ich etwas falsch oder habe ich einen Compiler Bug gefunden? Ich habe das Verhalten in C# 4 und C# 6 bestätigt.
@ManoDestra: Bearbeitungen, weil Sie nicht mögen, wo ich meine geschweiften Klammern platzieren, sind nicht willkommen. –
Dies ist C#. Daher die Bearbeitung. – ManoDestra
Ich verstehe Ihren Standpunkt nicht. –