2016-06-28 6 views
2

Ich habe Code wie folgt Wrapper:Converting Wrapper <Derived><Base>

class Base { } 

class Derived : Base { } 

class Wrapper<T> { 

    public T Value { get; } 

    public Wrapper (T value) { Value = value; } 
} 

Ich möchte Wrapper wie folgt verwenden:

Wrapper<Base> wrapper = new Wrapper<Derived> (new Derived()); 

Aber es endet mit diesem Fehler auf:

Error CS0029 Cannot implicitly convert type 'Wrapper<Derived>' to 'Wrapper<Base>'

Ich habe versucht, Methode in Wrapperzu erstellendas würde als Konverter funktionieren

Aber ich vermisse einige gültige Einschränkungen. Aktuelle Code endet mit Fehler auf:

S1503 Argument 1: cannot convert from 'T' to 'TResult'

I Einschränkungen To Methode vorstellen würde wie where T : TResult aussehen könnte, aber das ist nicht gültig Einschränkungen.

Alle Möglichkeiten, um Konverter von zu Wrapper<Base> einfach zu implementieren?

+1

Haben Sie versucht, 'Wrapper Wrapper = new Wrapper (neu Derived());'? – Eli

+0

Das würde funktionieren, wenn Sie im Voraus ein Objekt erstellen, aber in Fällen, in denen es so aussieht: 'var wrapper = new Wrapper (new Derived());/* Irgendein anderer Code/*/var newWrapper = Wrapper; 'wird es nicht. –

Antwort

4

Sie könnten Kovarianz wie so verwenden:

class Base { } 

class Derived : Base { } 

interface IWrapper<out T> 
{ 
    T Value { get; } 
} 

class Wrapper<T> : IWrapper<T> 
{ 
    public T Value { get; private set; } 

    public Wrapper(T value) { Value = value; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     IWrapper<Base> wrapper = new Wrapper<Derived>(new Derived()); 
    } 
} 
+0

Das sieht nach dem Weg aus. Natürlich möchte ich vermeiden, eine neue "Schnittstelle" einzuführen, aber wenn es keine anderen Vorschläge gibt, werde ich diesen Weg gehen. –

+0

Leider arbeiten Kovarianz und Kontravarianz nur mit Interfaces und Delegaten, daher müssen Sie den Interface-Ansatz verwenden, wenn Sie diese Methode verwenden möchten. – Tsef

+0

Ja, schon versuchte Covarianz auf 'Klasse', aber das hat nicht funktioniert. –

3

Zuerst würde ich eine Einschränkung der Klasse hinzufügen fordern, dass T muss Typ Base:

class Base { } 
class Derived : Base { } 

class Wrapper<T> where T : Base // T must be (derived from) Base 
{ 
    public T Value { get; } 

    public Wrapper (T value) { Value = value; } 
} 

Zweitens eine generische Wandler gefährlich wäre. Was passiert, wenn jemand versucht, eine Wrapper<Gnu> in eine Wrapper<Lion> umzuwandeln?

Also würde ich einen Schritt zurück und einen nicht-generic-Wandler machen, die einfach zu Wrapper<Base> konvertiert:

public Wrapper<Base> ToBase() 
{ 
    return new Wrapper<Base>(Value);  
} 

Und dies funktioniert, weil der Zwang für T auf Klassenebene.


C# ist eigentlich eine Sprache, die für ein hohes Maß an Sicherheit bekannt ist. Aber man kann es umgehen und tun, was Sie in dem Kommentar gefragt durch irgendwelche Zwänge ommitting und nur versuchen, zu werfen, was kommt in:

public Wrapper<TResult> To<TResult>() where TResult : class 
{ 
    return new Wrapper<TResult>(Value as TResult);  
} 

Sie benötigen den class Zwang und den as Operator, weil eine direkte Besetzung zwischen zwei generische Parameter sind nicht kompilierbar (da die IL zu sehr von den spezifischen Typen abhängt).

Aber dies wird Wrapper Instanzen mit Value auf null zurückgeben, wenn die Typen nicht übereinstimmen. Und es wird mit abgeleiteten Typen anstelle von Basistypen auch funktionieren. Also pass auf dich auf. Sie können dafür zusätzliche Prüfungen hinzufügen.Und kümmern sich um die Gnus :)


UPDATE:

Ein sicherer Weg:

public Wrapper<TResult> To<TResult>() where TResult : class// TResult must also be (derived from) Base 
{ 
    if (!typeof(TResult).IsAssignableFrom(typeof(T))) 
     throw new InvalidCastException(); 
    return new Wrapper<TResult>(Value as TResult); 
} 

Diese prüft, ob T von TResult abgeleitet ist und löst eine InvalidCastException, wenn nicht. Sie können das für Ihre Bedürfnisse verfeinern.

+0

Und wenn ich möchte, dass "Wrapper" generisch bleibt und jeden Typ annehmen kann? Ich möchte von 'Wrapper ' zu 'Wrapper ' konvertieren. –

+0

@DovydasSopa aktualisiere meine Antwort –

+0

Wie Sie mit zusätzlichen Kommentar hingewiesen, die einige Typüberprüfung erfordern würde, bevor Sie 'as' verwenden. –

0

Das Problem, das Sie stoßen sind, ist, dass die generische Typen Wrapper<Base> und Wrapper<Derived> sind zwei völlig verschiedene Klassen für .NET Framework

Was Sie tun können eine neue Wrapper des Typs schafft Base:

Wrapper<Base> wrapper = new Wrapper<Base>(new Derived()); 

Oder comple te Ihre To-Methode Ansatz:

public Wrapper<TResult> To<TResult>() where TResult : T 
    => new Wrapper<TResult>((TResult)Value); // This could throw an error 

public bool TryCastTo<TResult>(out Wrapper<TResult> derivedWrapper) where TResult : T 
{ 
    derivedWrapper = null; 

    // EDIT: changed to the version from René Vogt since this is much cleaner and mine had a little error 
    if (!typeof(T).IsAssignableFrom(typeof(TResult))) 
    { 
     return false; 
    } 

    derivedWrapper = new Wrapper<TResult>((TResult)Value); 
    return true; 
} 

Die Verwendung wäre:

Wrapper<Derived> derivedWrapper1 = wrapper.To<Derived>(); 
Wrapper<Derived> derivedWrapper2; 
bool success = wrapper.TryCastTo<Derived>(out derivedWrapper2);