2013-01-09 97 views
15

我是一個.NET人,所以讓我首先斷言我對幾個Java概念的理解 - 糾正我,如果我錯了。.Net等價於Java類型Class <>?

Java的泛型支持界通配符概念:

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

...這是類似的.NET where限制:

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

Java的Class類描述一個類型,大致爲等效於.NET Type

到目前爲止,這麼好。但是我找不到與Java通用類型Class<T>足夠接近的等價關係,其中T是有界通配符。這基本上對Class表示的類型施加了限制。

讓我舉個Java的例子。

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! 

我能得到.NET中最接近的是這樣的:

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); 

Java版本看起來比較清爽給我。 有沒有一種方法來改善.NET的對應?

+1

這可能有點太簡單了,但是不能簡單地將new()約束添加到where限制嗎?就像'MyExternalSort :IExternalSort where T:IExternalSort,new()',然後使用'var impl = new T();'?就我個人而言,我會使用工廠模式在這種情況下創建實現相同接口的類型的實例。 – Carsten

+0

我不太瞭解Java泛型,但我知道它們的實現方式不同(不支持JVM),並支持.NET泛型不具備的一些功能。所以不要指望找到一切的「等價」 – jalf

+0

@Aschratt我不明白這與我的問題有什麼關係。如果所有類型信息在編譯時已知,但此類技巧可能很有用,但類型MyExternalSort在運行時纔可知 - 它可能由我的排序庫的客戶端實現,並且僅由名稱指定。 –

回答

2

使用擴展方法&定製的包裝類System.Type,你可以非常接近Java語法。

注:Type.IsSubclassOf不能用來測試一個類型實現的接口 - 參閱MSDN上的鏈接文檔。可以使用Type.IsAssignableFrom代替 - 請參閱下面的代碼。使用接口方差

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); 
    } 
} 

進一步改進

(應該僅在生產代碼中使用的性能進行了評估後。可以通過使用(併發!)高速緩存字典ConcurrentDictionary<System.Type, IType<object>得到改善)

使用Covariant type parameters(使用C#4.0引入的功能)和其他類型interface IType<out T>(其中Type<T>實現),可以使以下幾種情況變爲可能:

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

甚至可以這樣做:

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); 
    } 
} 

,使人們可以(明確)無關的接口InterfaceAInterfaceB等之間的轉換:

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

但有點違背了演習的目的。

0

你可以使用「爲」運營商稍微漂亮的版本:

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

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

但在這裏我檢查它的類型,這可能是你的問題之前創建的實例。

+1

'as'運算符也執行演員。唯一的區別是,如果演員不成功,它不會拋出異常。這並不能讓事情變得更容易。 – Carsten

1

C#泛型是聲明站點方差,類型參數的方差是固定的。

Java是使用現場方差,所以一旦我們有一個聲明List<E>,我們可以用它3種方式

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

有利弊,這兩種方法。使用網站的方法顯然更加強大,儘管它對程序員來說太難了。我認爲它實際上是很容易掌握

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

不幸的是,Java的發明絕對可怕的語法,

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

一旦你的代碼中有幾個的,這些就變成真難看。你必須學會​​克服它。

現在,在申報網站陣營中,我們如何在同一個班級實現3個差異?通過具有更多類型 - 一個ReadOnlyList<out E>,一個WriteOnlyList<in E>和一個List<E>擴展兩個。這並不算太壞,有人會說這是一個更好的設計。但如果有更多的類型參數,它可能會變得很難看。如果一個班級的設計者沒有預料到它會被變化使用,那麼這個班級的用戶就無法變化地使用它。

+0

我想我現在明白你的答案是關於什麼的 - 儘管它與我的[這個(更新的)問題](http://stackoverflow.com/q/14277441/11545)更相關。也許你可以看看。 –

0

你可以嘗試寫一個擴展方法如下所示:

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; 
     } 
    } 

,然後可以用下面的方式使用:

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>(); 
相關問題