2016-03-22 13 views
2

Hier können wir die folgenden Klassen sagen:einen generischen Typ ohne Einschränkung bestanden mit mit einem eingeschränkten generischen Typ

public interface IFrobnicator<T> { } 

class ComparableFrobnicator<T> : IFrobnicator<T> where T : IComparable<T> 
{ 
    public ComparableFrobnicator(T value) { } 
} 

class EquatableFrobnicator<T> : IFrobnicator<T> where T : IEquatable<T> 
{ 
    public EquatableFrobnicator(T value) { } 
} 

Sie Methoden schreiben

public IFrobnicator<T> MakeFrobnicatorFromComparable<T>(T value) where T : IComparable<T> 
{ 
    return new ComparableFrobnicator<T>(value); 
} 

public IFrobnicator<T> MakeFrobnicatorFromEquatable<T>(T value) where T : IEquatable<T> 
{ 
    return new EquatableFrobnicator<T>(value); 
} 

Wenn ich das in einem einzigen vereinen wollte Methode, der naheliegendste Weg wird nicht kompilieren:

Das löst den folgenden Fehler beim Compi LED:

CS0314 Der Typ 'T' kann nicht als Typparameter 'T' im generischen Typ oder in der Methode 'UserQuery.ComparableFrobnicator' verwendet werden. Es gibt keine Box-Konvertierung oder Typ-Parameter-Konvertierung von 'T' nach 'System.IComparable'.

ich nicht new ComparableFrobnicator <T> mit new ComparableFrobnicator<IComparable<T>> ersetzen können, da die Probleme auf der ganzen Linie verursacht mit value wieder heraus bekommen - Sie können aus einem Schnittstellentyp auf einen konkreten Typ nicht gegossen.

Anstatt also ging ich den Weg der Reflexion:

public IFrobnicator<T> MakeFrobnicator<T>(T value) 
{ 
    if (value is IComparable<T>) 
    { 
     var constructor = typeof(ComparableFrobnicator<>).MakeGenericType(typeof(T)).GetConstructor(new[] { typeof(T) }); 
     return (IFrobnicator<T>)constructor.Invoke(new object[] { value}); 
    } 
    else if (value is IEquatable<T>) 
    { 
     var constructor = typeof(EquatableFrobnicator<>).MakeGenericType(typeof(T)).GetConstructor(new[] { typeof(T) }); 
     return (IFrobnicator<T>)constructor.Invoke(new object[] { value }); 
    } 
    else throw new ArgumentException(); 
} 

, die perfekt zu funktionieren scheint, aber sieht aus wie ein Schritt zurück in einer Sprache mit solchen leistungsfähigen Typinferenz als ich gewohnt bin. Gibt es etwas, das mir fehlt oder eine bessere Technik, die ich vermisst habe?

+0

In Ihrem kombinierten Verfahren zu lösen, ist Wert vom Typ T als Parameter, dann Sie sehen, ob der Wert ist IComparable oder IEquatable . Da stimmt etwas nicht. – David

+3

@ David z.B. 'int', ist' IComparable '* und *' IEquatable '. Ein Typ, der komplexe Zahlen repräsentiert, könnte mit sich selbst vergleichbar sein, aber nicht vergleichbar. – RoadieRich

+0

Es scheint, dass die letzte Methode auf die IComparable –

Antwort

2

die Erweiterung Methoden Ansatz versuchen, Die Art und Weise value.MakeFrobnicator() hilft implementiert generischen Typ params Ausgabe

public interface IFrobnicator<T> { } 

public class ComparableFrobnicator<T> :IFrobnicator<T> 
           where T :IComparable<T> 
{ 
    public ComparableFrobnicator(T param) { } 
} 

public class EquatableFrobnicator<T> :IFrobnicator<T> 
           where T : IEquatable<T> 
{ 
    public EquatableFrobnicator(T value) { } 
} 

public static class FrobnicatorExtentions 
{ 
    public static IFrobnicator<T> 
      MakeFrobnicatorFromComparable<T>(this T value) 
    where T: IComparable<T> 
    {                
     //return new ComparableFrobnicator<T>(value);  
    return value.MakeFrobnicator(); 
    } 

    public static IFrobnicator<T> 
      MakeFrobnicatorFromEquatable<T>(this T value) 
    where T : IEquatable<T> 
    { 
     // return new EquatableFrobnicator<T>(value); 
    return value.MakeFrobnicator(); 
    } 

    public static IFrobnicator<T> 
        MakeFrobnicator<T>(this IEquatable<T> value) 
    where T: IEquatable<T> 
    { 
     if (value is T) 
     { 
     if (value is IEquatable<T>) 
     { 
      return new EquatableFrobnicator<T>((T)value); 
     } 
     } 

    throw new InvalidCastException(); 
    } 

    public static IFrobnicator<T> 
       MakeFrobnicator<T>(this IComparable<T> value) 
    where T : IComparable<T> 
    { 
    if (value is T) 
    { 
     if (value is IComparable<T>) 
     { 
     return new ComparableFrobnicator<T>((T)value); 
     } 
    } 

    throw new InvalidCastException(); 
    } 

} 
+0

Das sieht so aus, als ob es funktioniert. Jetzt, um zu sehen, ob ich es auf meinen speziellen Fall erweitern kann. – RoadieRich

+0

Sie können den "Wert ist IComparable " etc etc entfernen: Sie sind immer wahr, durch die Methode Signatur. – RoadieRich

+0

@RoadieRich danke für die Annahme meiner Antwort, stimme zu 'Wert ist IComparable ' links von alten Refactoring-Schleife –

0

Haben Sie versucht, einen Cast zu machen?

public IFrobnicator<T> MakeFrobnicator<T>(T value) 
{ 
    if (value is IComparable<T>) 
    { 
     return new ComparableFrobnicator<T>((IComparable<T>)value); 
    } 
    else if (value is IEquatable<T>) 
    { 
     return new EquatableFrobnicator<T>((IEquatable<T>)value); 
    } 
    else throw new ArgumentException(); 
} 

Ich kann die Spitze von meinem Kopf erinnern ab, wenn das überhaupt gültig ist. Wie auch immer, es ist nicht sehr offen/geschlossen. Sie können stattdessen eine Liste von Konvertern erstellen, die entscheiden, ob sie für die Konvertierung geeignet sind.

+0

Das wird auch nicht kompiliert. 'ComparableFrobnicator ' ist das Problem. – RoadieRich

+0

Bei der Verwendung von 'is' werden dann zusätzliche Operationen ausgeführt.Verwenden Sie stattdessen 'var comparable = value als IComparable; wenn (vergleichbar! = null) '. – ErikE

Verwandte Themen