我是一个.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对应物?
答案 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);
}
}
这样就可以(明确地)在不相关的接口InterfaceA
和InterfaceB
之间进行转换,如:
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>();