用作更具体的泛型类型的通用类型

时间:2016-10-18 12:43:11

标签: c# generics

我有Class1和通用T1,Class2有通用T2。通用T2具有约束而T1不具有约束。

现在在Class1中,我想检查T1是否匹配T2的约束,以及它是否与Class2一起使用。

这可能吗?怎么样?

public class Class1<T1> {
    public static object GetObj() {
        if (typeof(SomeBaseClass).IsAssignableFrom(typeof(T1))) {
            // I know that T1 is a subclass of SomeBaseClass, and want to create a Class2 object using it
            return new Class2<T1>();  // this doesn't work. How can I "force" the compiler to treat T1 as a T2, like casting?
        }
    }
}

public class Class2<T2> where T2 : SomeBaseClass {
}

编辑:不幸的是,由于开销,反射不是一个好的解决方案,因此会被调用很多。

3 个答案:

答案 0 :(得分:4)

正如@AakashM所说,我猜使用反射是唯一的方法:

public class Class1<T1>
{
    public static object GetObj()
    {
        if (typeof(SomeBaseClass).IsAssignableFrom(typeof(T1)))
        {
            var genericType = typeof(Class2<>).MakeGenericType(typeof(T1));
            return Activator.CreateInstance(genericType);
        }

        // throw Exception or return null
    }
}

更新

根据您的评论,如果反射太慢,您可以使用编译的表达式:

public class Class1<T1>
{
    private static readonly Func<T1> _factory;

    static Class1() 
    {
        if (!typeof(SomeBaseClass).IsAssignableFrom(typeof(T1)))
        {
            _factory = () => default(T1);
            return;
        }

        var ctor = typeof(T1).GetConstructor(Array.Empty<Type>());
        var newExpression = Expression.New(ctor);

        _factory = Expression.Lambda<Func<T1>>(newExpression).Compile();
    }

    public static object GetObj()
    {
        return _factory();
    }
}

此解决方案为Class1的每个变体创建工厂(例如Class1<SomeSubClass>Class1<SomeOtherSubClass>等等)。构建表达式需要一些时间,但之后它真的很快。

我已经运行了一些测试,看看使用Class2方法创建100万个GetObj()个实例需要多长时间:

  • 最佳情况(约束已添加到Class1,只需返回new Class2<T1>()): 4毫秒
  • 反射(如原始解决方案中所示): 991 ms
  • 使用已编译的表达式(如上所示): 26 ms

正如您所看到的,新解决方案明显快于前一个解决方案。但是,如果您为T1使用了许多不同的类型,则性能提升会更小甚至不存在。

答案 1 :(得分:0)

如果我理解正确,T1和T2都继承自SomeBaseClass? 如果是,则应将Class1声明为

public class Class1<T1> where T1: SomeBaseClass

答案 2 :(得分:0)

根据要求,您可以使用两种不同的通用方法,其中一种约束:

public static object GetObj<T>() where T : SomeBaseClass => new Class2<T>();
public static object GetObjOther<T>() => <whatever you want as else>;

没有静态类型的方法来实现它,方法与类型系统在编译时需要知道的方法相同;如果您必须按照描述使用if语句,则唯一的另一种方式是fknx的答案中建议的反映。

如果您添加特定类型的参数,则越接近您的要求:

public static object GetObj<T>(T obj) where T : SomeBaseClass => new Class2<T>();
public static object GetObj<T>(Class2<T> objc) where T : SomeBaseClass => <whatever you want as else>;

当然,如果不了解背景,这确实令人困惑,可能是错误的。