Java类型的类的.Net等效<&gt ;?

时间:2013-01-09 14:10:50

标签: java .net generics casting

我是一个.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对应物?

4 个答案:

答案 0 :(得分:2)

使用扩展方法&amp; 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;

但这有点违背了练习的目的。

答案 1 :(得分: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>两种类型。这不是太糟糕,人们可能会说这是一个更好的设计。但如果有更多类型参数,它可能会变得丑陋。如果一个类的设计者没有预料到它会被不同地使用,那么该类的用户就无法可变地使用它。

答案 2 :(得分:0)

使用“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(...);

但是在这里我在创建实例之前检查它的类型,这对你来说可能是一个问题。

答案 3 :(得分: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>();