2015-09-23 6 views
5

Ich schreibe zwei Erweiterungsmethoden. Eine, um an einem einzelnen Objekt zu arbeiten, und eine, um an einer Sammlung von Objekten zu arbeiten. Beim Aufrufen der Erweiterungsmethode scheint der C# -Compiler verwirrt zu werden, welcher zu verwenden ist, und die Kompilierung schlägt fehl. Wenn ich die Erweiterungsmethoden in andere Namespaces verschiebe, schlägt die Kompilierung nur fehl, wenn die Namespaces alphabetisch in einer bestimmten Reihenfolge sind - wenn ich die Namespaces in die Callsite einschließe, führt das Umschalten der Namespaces dazu, dass die Kompilierung erfolgreich ist. HierC# Erweiterung Method Compilation/Kompatibilitätsüberprüfungen fehlgeschlagen basierend auf der Reihenfolge der Namespaces

ist der Code:

public static class DBObjectExtensions 
{ 
    public static void PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
    { 
     if (obj == null) 
     { 
      return; 
     } 

     obj.Transaction.PopulateRelations<T>(new[]{ obj }, relationsToPrefetch); 
    } 

    public static void PopulateRelations<T>(this IEnumerable<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
    { 
     var first = objects.FirstOrDefault(); 
     if (first == null) 
     { 
      return; 
     } 

     first.Transaction.PopulateRelations<T>(objects, relationsToPrefetch); 
    } 
} 

Dies ist die callsite Linie, die Kompilierung fehlschlägt:

List<ITable> list = ... // ITable inherits from IDBObject 
list.PopulateRelations(xxx); 

schlägt mit Fehler CS0311:

Der Typ ‚System.Collections. Generic.List 'kann nicht als Typparameter' T 'im generischen Typ oder in der Methode' Granta.MI.DBObjectExtensions.PopulateRelations (T, params Granta.MI. RelationToPrefetch []) '. Es gibt keine implizite Referenzkonvertierung von 'System.Collections.Generic.List' nach 'Granta.MI.IDBObject'.

Beachten Sie, dass diese Zeile nach dem Kompilieren erfolgreich ist, wenn ich die 2. Erweiterungsmethode lösche.

Beachten Sie auch, dass das Schreiben Trampolin Methoden (für jede mögliche Art der Sammlung ...) auch funktioniert:

public static void PopulateRelations<T>(this List<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
{ 
    ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch); 
} 

public static void PopulateRelations<T>(this IList<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject 
{ 
    ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch); 
} 

Warum kann der Compiler Figur gibt eine passende Erweiterung Methode? Und verwirrender, wenn ich eine der Methoden in einen anderen Namespace lege und diesen Namespace einschließe, warum ist die Kompilierung dann erfolgreich? Kann ich etwas tun, um das zu beheben?

+0

Die Überladungsauflösung berücksichtigt keine Einschränkungen. Außerdem scheint es nicht so, dass Sie 'T' innerhalb der Methodenfunktion benötigen. Warum brauchen Sie' T' für Ihre Erweiterung, anstatt nur 'IDbObject' zu verwenden? – Jcl

+0

@Jcl Leute werden sehr oft mit der Verwendung von 'T' verwirrt, wie Sie gesagt haben, wenn Sie' T' innerhalb der Erweiterungsmethode nicht brauchen, ändern Sie einfach die Methoden wie in der @ jakub-lortz Antwort. – mijail

+0

Entschuldigung - Ich habe den Grund dafür nicht in meiner Antwort angegeben.Der Grund ist, dass 'Transaction.PopulateRelations' ebenfalls einen Typ T annimmt und dann' typeof' aufruft, so dass ich brauche, dass T ein konkreter Typ ist, nicht nur 'IDBObject'. –

Antwort

2

Generische Constraints sind nicht Teil der Methodensignatur, daher wählt der Compiler , weil T mehr abgeleitet ist als IEnumerable<T>.

Beispiel, zwischen diesen zwei Methoden:

public static void PopulateRelations(this List<ITable> obj, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    // Do something 
} 

public static void PopulateRelations(this IEnumerable<ITable> objects, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    // Do something 
} 

Die erste gewählt wird beim Aufruf:

List<ITable> list; 
PopulateRelations(list, something); // Not calling as extension method to more clear 

Da list Matches direkt mit List<ITable>

+0

Eric Lippert schrieb einen nützlichen Blog-Post zu diesem Thema: http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx –

+0

Warum ist die Kompilierung dann erfolgreich, wenn ich die Namespaces neu ordne? –

+0

@MichaelParker Wie bestellen Sie Namespaces? –

0

Ihre generische Typen auf IDBObject eingeschränkt sind , so könnten Sie einfach die Erweiterungsmethoden nicht-generisch machen:

public static void PopulateRelations(this IDBObject obj, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    //... 
} 

public static void PopulateRelations(this IEnumerable<IDBObject> objects, params RelationToPrefetch[] relationsToPrefetch) 
{ 
    //... 
} 

Es löst den Kompilierungsfehler.

+0

Nicht ganz, weil sie an eine generische Methode der Transaktion namens PopulateRelations delegieren, die das T verwendet und 'typeof' auf T aufruft. Ich weiß, dass ich dies nicht in meine ursprünglichen Post - Entschuldigungen aufgenommen habe. –