【问题标题】:.Net equivalent for Java typed Class<>?.Net 等效于 Java 类型的 Class<>?
【发布时间】:2013-01-09 14:10:49
【问题描述】:

我是一个 .NET 人,所以让我首先声明我对一些 Java 概念的理解 - 如果我错了,请纠正我。

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

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

...类似于 .NET 的where 限制:

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

Java 的 Class 类描述了一种类型,大致等效于 .NET Type

到目前为止,一切都很好。但是我找不到与 Java 通用类型 Class&lt;T&gt; 足够接近的等价物,其中 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 对应项?

【问题讨论】:

  • 这可能有点太简单了,但是不能简单地将new() 约束添加到 where 限制中吗?类似MyExternalSort&lt;T&gt; : IExternalSort where T : IExternalSort, new() 然后使用var impl = new T();?我个人会使用工厂模式来创建在这种情况下实现相同接口的类型的实例。
  • 我对 Java 泛型了解不多,但我知道它们的实现方式不同(不支持 JVM),并且支持许多 .NET 泛型不支持的东西。所以不要指望为所有事情找到一个直接的“等价物”
  • @Aschratt 我不明白这与我的问题有什么关系。如果在编译时知道所有类型信息,则此技巧可能很有用,但类型 MyExternalSort 直到运行时才知道 - 它很可能由我的排序库的客户端实现,并且仅由名称指定。
  • @jalf 我知道类型擦除,所以我当然不期望 1:1 映射。我只是希望对我目前不太漂亮的实现有所改进。
  • @Cristi Diaconescu:编译时不需要知道专门的类型。它只需要在编译时适应您的约束(从 IExternalSort 继承,无参数构造函数),因此当您不知道它的确切类型时,您还可以提供一些参数作为 IExternalSort 实例。

标签: java .net generics casting


【解决方案1】:

使用 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&lt;System.Type, IType&lt;object&gt; 来改进)

使用Covariant type parameters(C# 4.0 引入的一项功能)和Type&lt;T&gt; 实现的附加类型interface IType&lt;out T&gt;,可以使以下事情成为可能:

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

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

【讨论】:

    【解决方案2】:

    C# 泛型是声明点的变体,类型参数的变体是固定的。

    Java 是使用点变异,所以一旦我们声明了List&lt;E&gt;,我们可以通过 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&lt;out E&gt;WriteOnlyList&lt;in E&gt;List&lt;E&gt; 扩展两者。这还不错,有人可能会说这是一个更好的设计。但是如果有更多的类型参数,它可能会变得丑陋。如果一个类的设计者没有预料到它会被多变地使用,那么这个类的用户就没有办法多变地使用它。

    【讨论】:

    • 我想我现在明白你的答案是什么了——尽管它与我的this (newer) question 更相关。也许你可以看看。
    【解决方案3】:

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

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

    【讨论】:

    • as 运算符也执行强制转换。唯一的区别是,如果强制转换不成功,它不会抛出异常。这并没有真正让它变得更容易。
    【解决方案4】:

    您可以尝试编写如下扩展方法:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      • 2012-11-28
      • 2010-11-20
      • 2019-04-15
      • 2011-01-27
      • 2015-08-21
      • 2010-09-06
      相关资源
      最近更新 更多