2013-01-09 7 views
15

Ich bin ein .NET-Typ, also lass mich zuerst mein Verständnis einiger Java-Konzepte durchsetzen - korrigiere mich, wenn ich falsch liege.. Net-Entsprechung für Java typisierte Klasse <>?

Java Generics unterstützen das Konzept der beschränkten Platzhalter:

class GenericClass< ? extends IInterface> { ... } 

... die mit dem .NET where Einschränkung ähnelt:

class GenericClass<T> where T: IInterface { ... } 

Java Class Klasse beschreibt einen Typ und ist grob entspricht. NET Type Klasse

So weit, so gut. Aber ich kann keine nahe genug Entsprechung zu Java generisch typisierten Class<T> finden, wobei T eine begrenzte Wildcard ist. Dies bedeutet grundsätzlich eine Einschränkung für die Typen, die der Class darstellt.

Lassen Sie mich ein Beispiel in Java geben.

String custSortclassName = GetClassName(); //only known at runtime, 
              // e.g. it can come from a config file 
Class<? extends IExternalSort> customClass 
    = Class.forName("MyExternalSort") 
     .asSubclass(IExternalSort.class); //this checks for correctness 

IExternalSort impl = customClass.newInstance(); //look ma', no casting! 

Der nächstgelegene ich in .NET ist so etwas wie diese bekommen könnte:

String custSortclassName = GetClassName(); //only known at runtime, 
              // e.g. it can come from a config file 

Assembly assy = GetAssembly();    //unimportant 

Type customClass = assy.GetType(custSortclassName); 
if(!customClass.IsSubclassOf(typeof(IExternalSort))){ 
    throw new InvalidOperationException(...); 
} 
IExternalSort impl = (IExternalSort)Activator.CreateInstance(customClass); 

Die Java-Version sieht sauberer zu mir. Gibt es eine Möglichkeit, das .NET-Gegenstück zu verbessern?

String custSortclassName = GetClassName(); 
Assembly assy = GetAssembly(); 
Type customClass = assy.GetType(custSortclassName); 

IExternalSort impl = Activator.CreateInstance(customClass) as IExternalSort; 
if(impl==null) throw new InvalidOperationException(...); 

Aber hier Ich schaffe die Instanz vor der Überprüfung seiner Art, das ein Problem für Sie sein:

+1

Das mag ein bisschen zu einfach sein, aber wäre es nicht möglich, einfach die Einschränkung 'new()' zur Beschränkung where hinzuzufügen? So etwas wie 'MyExternalSort : IExternalSort wo T: IExternalSort, new()' und dann 'var impl = new T();'? Persönlich würde ich das Factory-Muster verwenden, um Instanzen von Typen zu erstellen, die die gleiche Schnittstelle in solchen Szenarien implementieren. – Carsten

+0

Ich weiß nicht viel über Java-Generics, aber ich weiß, dass sie anders implementiert sind (keine JVM-Unterstützung) und eine Reihe von Dingen unterstützen, die .NET-Generics nicht unterstützen. Also erwarte nicht, ein geradliniges "Äquivalent" für alles zu finden – jalf

+0

@Aschratt Ich sehe nicht, wie das für meine Frage relevant ist. Dieser Trick könnte nützlich sein, wenn alle Typinformationen zur Kompilierzeit bekannt sind, aber der Typ MyExternalSort ist erst zur Laufzeit bekannt - er kann von einem Client meiner Sortierbibliothek implementiert werden und wird nur anhand des Namens angegeben. –

Antwort

2

Mit Erweiterungsmethoden & eine benutzerdefinierte Wrapper-Klasse für System.Type können Sie der Java-Syntax ziemlich nahe kommen.

HINWEIS:Type.IsSubclassOf kann nicht getestet werden, wenn ein Typ eine Schnittstelle implementiert - siehe die verknüpfte Dokumentation auf MSDN. Man kann stattdessen Type.IsAssignableFrom verwenden - siehe unten stehenden Code.

using System; 

class Type<T> 
{ 
    readonly Type type; 

    public Type(Type type) 
    { 
     // Check for the subtyping relation 
     if (!typeof(T).IsAssignableFrom(type)) 
      throw new ArgumentException("The passed type must be a subtype of " + typeof(T).Name, "type"); 

     this.type = type; 
    } 

    public Type UnderlyingType 
    { 
     get { return this.type; } 
    } 
} 

static class TypeExtensions 
{ 
    public static Type<T> AsSubclass<T>(this System.Type type) 
    { 
     return new Type<T>(type); 
    } 
} 

// This class can be expanded if needed 
static class TypeWrapperExtensions 
{ 
    public static T CreateInstance<T>(this Type<T> type) 
    { 
     return (T)Activator.CreateInstance(type.UnderlyingType); 
    } 
} 

Weitere Verbesserungen Schnittstelle Varianz

(Sollte nur in Produktionscode verwendet werden, nachdem die Leistung bewertet wurde. Kann durch die Verwendung eines (concurrent!) Cache-Wörterbuch ConcurrentDictionary<System.Type, IType<object> verbessert werden)

Mit Covariant type parameters, einem Feature, das mit C# 4.0 eingeführt wurde, und einem zusätzlichen Typ interface IType<out T>, der Type<T> implementiert, könnte man folgende Dinge möglich machen:

// IExternalSortExtended is a fictional interface derived from IExternalSort 
IType<IExternalSortExtended> extendedSort = ... 
IType<IExternalSort> externalSort = extendedSort; // No casting here, too. 

Man könnte sogar tun:

using System; 

interface IType<out T> 
{ 
    Type UnderlyingType { get; } 
} 

static class TypeExtensions 
{ 
    private class Type<T> : IType<T> 
    { 
     public Type UnderlyingType 
     { 
      get { return typeof(T); } 
     } 
    } 

    public static IType<T> AsSubclass<T>(this System.Type type) 
    { 
     return (IType<T>)Activator.CreateInstance(
      typeof(Type<>).MakeGenericType(type) 
     ); 
    } 
} 

static class TypeWrapperExtensions 
{ 
    public static T CreateInstance<T>(this IType<T> type) 
    { 
     return (T)Activator.CreateInstance(type.UnderlyingType); 
    } 
} 

so dass man (explizit) Guss zwischen nicht verwandten Schnittstellen InterfaceA und InterfaceB wie:

var x = typeof(ConcreteAB).AsSubclass<InterfaceA>(); 
var y = (IType<InterfaceB>)x; 

aber irgendwie besiegt den Zweck der Übung .

0

Sie können eine etwas hübschere Version des „als“ Operator erhalten.

+1

Der 'as' Operator führt auch eine Besetzung durch. Der einzige Unterschied besteht darin, dass keine Ausnahme ausgelöst wird, wenn die Besetzung nicht erfolgreich war. Das macht es nicht wirklich einfacher. – Carsten

1

C# -Generika ist die Varianz der Deklarationsstelle, die Varianz eines Typparameters ist festgelegt.

Java ist die Verwendung vor Ort Varianz, also, sobald wir eine Erklärung List<E> haben, können wir es 3 Möglichkeiten

List<Number>   // invariant, read/write 
List<+Number>   // covariant, read only 
List<-NUmber>   // contravariant, write only 

Es verwenden, sind Vor-und Nachteile beider Ansätze. Der Use-Site-Ansatz ist offenbar mächtiger, obwohl er den Ruf erworben hat, zu schwierig für Programmierer zu sein. Ich denke, es ist eigentlich ziemlich einfach ist

List<Integer> integers = ...; 
List<+Number> numbers = integers; // covariant 

Leider zu erfassen, Java eine absolut scheußlich Syntax erfunden,

List<? extends Number> // i.e. List<+Number> 

sobald Ihr Code mehr von mir hat es wird wirklich hässlich. Sie müssen lernen, darüber hinweg zu kommen.

Jetzt, in der Declaration-Site Camp, wie erreichen wir 3 Varianzen in der gleichen Klasse? Indem Sie mehr Typen haben - eine ReadOnlyList<out E>, eine WriteOnlyList<in E>, und eine List<E>, die beides erweitert. Das ist nicht schlecht, und man könnte sagen, es ist ein besseres Design. Aber es kann hässlich werden, wenn es mehr Typparameter gibt. Und wenn der Designer einer Klasse nicht vorausgesehen hat, dass er variabel verwendet wird, haben die Benutzer der Klasse keine Möglichkeit, sie variabel zu verwenden.

+0

Ich glaube, ich verstehe jetzt, worum es in deiner Antwort geht - obwohl es mehr mit [dieser (neueren) Frage] (von mir) zu tun hat. (Http://stackoverflow.com/q/14277441/11545). Vielleicht kannst du einen Blick darauf werfen. –

0

können Sie versuchen, eine Erweiterungsmethode wie folgt zu schreiben:

static class TypeExtension 
    { 
     public static I NewInstanceOf<I>(this Type t) 
      where I: class 
     { 
      I instance = Activator.CreateInstance(t) as I; 
      if (instance == null) 
       throw new InvalidOperationException(); 
      return instance; 
     } 
    } 

, die dann auf die folgende Weise verwendet werden:

String custSortclassName = GetClassName(); //only known at runtime, 
              // e.g. it can come from a config file 

Assembly assy = GetAssembly(); 
Type customClass = assy.GetType(custSortclassName);    

IExternalSort impl = customClass.NewInstanceOf<IExternalSort>(); 
Verwandte Themen