2016-04-01 23 views
2

Sie können nicht Kovarianz für Werttypen verwenden:Wie wird die Kovarianz für Werttypen verwendet?

Func<string> refTypeFunc =() => "foo"; 
Func<int> valueTypeFunc =() => 123; 

Func<object> a = refTypeFunc; // works 
Func<object> b = valueTypeFunc; // doesn't work 

This answer by Jon Skeet erklärt, warum:

Grundsätzlich Varianz gilt, wenn die CLR sicherstellen kann, dass es keine gegenständlich Veränderung zu machen braucht zu den Werten. Alle Referenzen sehen gleich aus - Sie können also IEnumerable<string> als IEnumerable<object> ohne Änderung der Darstellung verwenden; Der native Code selbst muss nicht wissen, was Sie mit den Werten überhaupt machen, solange die Infrastruktur garantiert hat, dass sie definitiv gültig ist.

Bei Werttypen funktioniert das nicht - um einen IEnumerable<int> als IEnumerable<object> zu behandeln, müsste der Code, der die Sequenz verwendet, wissen, ob eine Box-Konvertierung durchgeführt werden soll oder nicht.

Nun, kacken. Zumindest für Func können Sie dies tun:

Func<object> c =() => valueTypeFunc(); 

jedoch so eine einfache Art und Weise können Sie das nicht für die meisten Fälle verwendet werden. Sagen, ich habe eine Schnittstelle wie folgt definiert:

interface ICovariant<out T> 
{ 
    Func<T> InnerFunc { get; } 
} 

Nun, wenn ich eine ICovariant<T> haben, ich es nicht ICovariant<object> werfen kann, und ich sehe keinen einfachen Weg aus ihm heraus. Ich weiß T kann ein object sein - alles kann. Was kann ich in diesem Fall tun? Wenn es keine einfache Problemumgehung gibt, was wäre sonst der beste Ansatz?

+0

Vielleicht fragen Sie sich, warum Ihr Entwurf Sie braucht generische Werte 'object' zu werfen, die im Grunde ist das Besiegen der gesamte Zweck von Generika an erster Stelle. – juharr

+0

@juharr Sowohl die Implementierung als auch die Verwendung ist bei Verwendung von Generika viel sauberer. Es gibt eine Methode, die 'T' zurückgibt - das Zurückgeben von' object' würde eine Menge hässlichen Downcasting im Aufrufcode hinzufügen. Und "T" als ein "Objekt" zu verwenden wäre sehr nützlich für Polymorphie, sagen wir viele meiner Objektinstanzen zu einer Liste hinzufügen. –

+0

Wenn Sie sicherstellen möchten, dass es keine gegenständlichen Änderungen gibt, kleben Sie einfach eine 'where T: Klasse' dort hinein. –

Antwort

2

Sie müssen eine spezielle Implementierung Ihrer kovarianten Schnittstelle vornehmen, um diese Konvertierung für Sie durchzuführen. Etwas wie folgt aus:

public class Boxer<T, U> : ICovariant<T> where U : struct, T 
{ 
    public Boxer(ICovariant<U> foo) 
    { 
     mFoo = foo; 
    } 

    public Func<T> CallMe =>() => mFoo.CallMe(); 

    private readonly ICovariant<U> mFoo; 
} 

Dies ermöglicht es Ihnen jetzt wert Typ Implementierungen der ICovariant<T> Schnittstelle zu wickeln. Wenn Sie alle generischen Parameter widerwärtig eingeben finden, können Sie eine statische Methode für Sie erstellen, den Abzug zu tun:

static void BoxIt<T, U>(IFoo<U> fooU, out IFoo<T> fooT) where U : struct, T 
{ 
    fooT = new Boxer<T, U>(fooU); 
} 
Verwandte Themen