2013-04-03 3 views
6

Ich habe ein seltsames Problem, wenn ich versuche, ein Objekt über eine Erweiterungsmethode zu übertragen. Ich habe eine Klasse, in der ich etwas Funktionalität um eine IPAddress wickle.Explizite Umwandlung in eine Erweiterungsmethode verwenden

// Dumbed down version of class 
public sealed class PrefixLengthIPAddress 
{ 
    public static explicit operator IPAddress(PrefixLengthIPAddress address) 
    { 
     return (address != null) ? address._address : null; 
    } 

    public PrefixLengthIPAddress(IPAddress address) 
    { 
     _address = address; 
     _length = address.GetLength(); 
    } 

    private readonly ushort _length; 
    private readonly IPAddress _address; 
} 

Ich mag es nicht das Aussehen der alle Klammern die IPAddress aus dem Objekt zu extrahieren:

var family = ((IPAddress)prefixLengthAddress).AddressFamily; 

würde ich eher der Lage sein, so etwas zu tun:

var family = prefixLengthAddress.CastAs<IPAddress>().AddressFamily; 

Um dies zu tun, schrieb ich die folgende Erweiterungsmethode:

public static T CastAs<T>(this object value) where T : class 
{ 
    return (T)value; 
} 

Leider damit ich bin immer ein InvalidCastException:

var family = ((IPAddress)prefixLengthAddress).AddressFamily;   // Works 
var family = prefixLengthAddress.CastAs<IPAddress>().AddressFamily; // InvalidCastException 

Ich verstehe, dass ich in diesem speziellen Fall einfach die IPAddress mit einem Getter aussetzen könnte, aber wir haben auch komplexere explizite Casts, die Ich mag würde zu mach das mit.

EDIT

Dank Kommentar Chris Sinclair auf dynamic ich mit der Erweiterungsmethode aktualisiert aussehen:

public static T CastAs<T>(this object value) 
{ 
    return (T)((dynamic)value); 
} 

Es gibt einige Overhead ist dynamic mit der Verwendung, aber es ist mehr als schnell genug für meine Bedürfnisse. Es scheint auch mit allen grundlegenden Casting-Typen zu funktionieren, die ich ausprobiert habe.

+0

Nun, 'PrefixLengthIPAddress' hat keine Beziehung in der Typhierarchie zu' IPAddress', die die ultimative Quelle des Problems ist. In welcher anderen Situation würden Sie diesen Casting-Code verwenden? Der Versuch, eine Instanz des Typs A willkürlich auf eine Instanz des Typs B zu erzwingen, ist fehlerhaft. Darüber hinaus ist das Generische tatsächlich unbegrenzt; So könnte 'T' eine' Zeichenfolge' sein, und es kann keine Überladung des Operators auftreten. Ich vermute, dass sogar die Beschränkung auf "IPAddress" für "T" nicht zu dem von Ihnen erwarteten Verhalten führt, und selbst wenn dies der Fall ist, ist Ihre Erweiterung ansonsten nutzlos. – Tejs

+0

Ich mag einfach das Aussehen der Erweiterungsmethode besser. Wenn ich versuchen würde, mein Objekt als String zu erzeugen, würde ich erwarten, dass es eine InvalidCastException wirft. – lumberjack4

Antwort

7

Im ersten Beispiel greifen Sie auf die benutzerdefinierte Konvertierung zu. Dies ist nur verfügbar, wenn der Darsteller den Typ der Eingabe PrefixLengthAddress kennt. Im generischen Code kennt der Compiler nur den Typ object und T. Es gibt keine Möglichkeit, auf die in diesem Szenario unter PrefixLengthAddress definierte Konvertierung zuzugreifen.

In diesem Szenario ist das, was Sie tun, viel näher an Mapping vs Casting, da es tatsächlich einen neuen Wert erstellt. In Bezug auf LINQ möchten Sie Select vs. Cast verwenden.

+0

Die Ausnahmebedingungsnachricht, die in der Erweiterungsmethode ausgelöst wird, lautet: "Objekt vom Typ 'PrefixLengthIPAddress' kann nicht in den Typ 'System.Net.IPAddress'" umgewandelt werden. Wie also kennt es den Typ in diesem Fall nicht? – lumberjack4

+0

@ Lumberjack4 benutzerdefinierte Konvertierungen werden nur vom Sprachencompiler (in diesem Fall C#) verwendet. Um verwendet zu werden, müssen die Typen zum Zeitpunkt der Kompilierung bekannt sein. Was Sie sehen, ist die Laufzeit, die besagt, dass zwischen zwei Typen keine Vererbungsbeziehung besteht. Die Laufzeit berücksichtigt keine benutzerdefinierten Konvertierungen. – JaredPar

+0

Wie funktioniert der Linq 'Enumerable.CastIterator 'Arbeit dann? Wenn ich in diesem Code aussehen sieht es fast identisch mit Grube: private static IEnumerable CastIterator (IEnumerable Quelle) { foreach (object obj in Quelle) yield return (TResult) obj; } – lumberjack4