[英].Net equivalent for Java typed Class<>?
我是一個.NET人,所以讓我首先斷言我對一些Java概念的理解 - 如果我錯了,請糾正我。
Java Generics支持有界通配符的概念:
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版本?
使用擴展方法和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引入的功能,以及Type<T>
實現的附加類型interface IType<out 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);
}
}
因此,可以(明確地)在不相關的接口InterfaceA
和InterfaceB
之間進行轉換,如:
var x = typeof(ConcreteAB).AsSubclass<InterfaceA>();
var y = (IType<InterfaceB>)x;
但這有點挫敗了演習的目的。
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>
擴展兩者。 這不是太糟糕,人們可能會說這是一個更好的設計。 但如果有更多類型參數,它可能會變得丑陋。 如果一個類的設計者沒有預料到它會被不同地使用,那么該類的用戶就無法可變地使用它。
您可以使用“as”運算符獲得稍微漂亮的版本:
String custSortclassName = GetClassName();
Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);
IExternalSort impl = Activator.CreateInstance(customClass) as IExternalSort;
if(impl==null) throw new InvalidOperationException(...);
但是在這里我在創建實例之前檢查它的類型,這可能是一個問題。
您可以嘗試編寫如下擴展方法:
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>();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.