簡體   English   中英

Java類型Class <>的.Net等價物?

[英].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);
    }
}

因此,可以(明確地)在不相關的接口InterfaceAInterfaceB之間進行轉換,如:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM