【问题标题】:How to Instantiate a Generic Type With Multiple Input Parameters?如何实例化具有多个输入参数的泛型类型?
【发布时间】:2011-06-16 14:59:53
【问题描述】:

我有以下两种方法:

Method1(int a, int b)
{
    var type = Typ1(a, b);
}

Method2
{
    var type = Typ2(a, b);
}

我想写一个通用的方法来完成这项工作:

GenericMethod<T>(int a, int b)
{
    var type = new T(a, b);
}

但是 T 不接受任何输入参数。我怎样才能做到这一点?

我知道使用Activator.Instance(T, a, b) 可以做到这一点,但它的性能成本很高。

我也知道我可以使用 T() 调用泛型类型的默认构造函数,然后设置属性,但在我的情况下,我想传递 2 个强制性参数。

我不想引入没有参数的构造函数。

有没有办法用泛型做到这一点?

谢谢,

【问题讨论】:

  • 应该注意的是,即使你在一个泛型类型上执行new T(),它是一个new() 约束,编译器也会简单地将它转换为一个Activator.CreateInstance&lt;T&gt;() 调用,所以没有性能益处。解决这个问题的唯一方法是使用某种工厂模式,类似于 SLaks 对委托所做的。

标签: c# generics


【解决方案1】:

创建你的工厂类:

    public static class TypeFactory<T>
{
    private static Func<int, int, T> Func { get; set; }

    static TypeFactory()
    {
        TypeFactory<Type1>.Func = (a, b) => new Type1(a, b);
        TypeFactory<Type2>.Func = (a, b) => new Type2(a, b);
    }

    public static T Create(int a, int b)
    {
        return Func(a, b);
    }
}

然后像这样使用它:

        var type1 = TypeFactory<Type1>.Create(1, 2);
        var type2 = TypeFactory<Type2>.Create(1, 2);

【讨论】:

  • 你不应该在类本身中初始化它。您的初始化程序将为每个类型参数运行一次。
  • 那你有什么建议?我可以在类中使用锁来确保静态构造函数只运行一次,但我怀疑它会比 Activator.CreateInstance() 获得任何性能优势?
  • 你有这方面的代码示例吗?我不能只是将静态 ctor 移出课堂。我的意思是我可以移动它,但是应该什么时候调用它以及从哪里调用它?我只需要在整个应用程序中调用它一次。我可以把它放在 Application_Start 事件中!
【解决方案2】:

没有。

相反,您可以接受为您创建它们的委托:

GenericMethod<T>(int a, int b, Func<int, int, T> creator) {
    T t = creator(a, b);
}

GenericMethod(8, 9, (a, b) => new YourType(a, b));

你也可以store these creators in a generic static class:

static class Creator<T> {
    public static Func<int, int, T> Func { get; set; }
}
GenericMethod<T>(int a, int b) {
    T t = Creator<T>.Func(a, b);
}


Creator<YourType>.Func = (a, b) => new YourType(a, b);

【讨论】:

  • 我非常喜欢静态泛型类方法,谢谢。与其他方法相比,它是否具有最高的性能?
【解决方案3】:

如果您不想使用 Activator,可以使用表达式树。 Incorrect number of parameters supplied for lambda declaration

【讨论】:

  • 为了让这件事变得有价值,您需要存储已编译的表达式树(例如,在静态通用字典中,如我的回答中所示),以便您以后可以重用它。否则,它最终会变得更慢。
【解决方案4】:

理论上,您需要使用generic type constraint。但是,唯一可用的构造函数约束是支持无参数构造函数where T : new()

如果 Typ1Typ2 共享一个基类,该基类使用 2 个整数定义属性,或者两者都支持接口保证这些整数的设置器,您可以在每个类上定义无参数构造函数并使用附加约束以允许以后访问到属性。

【讨论】:

  • T 对基类型的约束不会打开 T 上的构造函数。一方面,不能保证对于具有参数化构造函数的基类,派生的子类也 i> 提供参数化构造函数。
  • 即使有new() 约束,编译器也只是将其转换为对Activator.CreateInstance&lt;T&gt;() 的调用,因此没有性能提升。您唯一获得的是编译时检查构造函数的存在。
  • @Sven,你从哪里得到的?我在 IL 或 C# 语言规范中没有看到关于约束的此类证据。
  • 我不确定这是否是规范所要求的行为,但 Microsoft 和 Mono C# 编译器都是这样做的;你可以用 ILSpy 的 Reflector 或类似的东西来检查它。
  • @Sven,SLaks,你知道吗,没关系。我测试了错误的方法。我看到return new T()return Activator.CreateInstance&lt;T&gt;() 上的IL 是一样的。 return (T)Activator.CreateInstance(typeof(T)) 明显不同。
【解决方案5】:
public static class MyTypeFactory
{
    static MyTypeFactory()
    {
        MethodRunner<Type1>.Func = (a, b) => new Type1(a, b);
        MethodRunner<Type2>.Func = (a, b) => new Type2(a, b);
    }

    public static T Create<T>(int a, int b)
    {
        return MethodRunner<T>.Func(a, b);
    }

    static class MethodRunner<T>
    {
        public static Func<int, int, T> Func { get; set; }
    }
}

这看起来很有希望?!

静态 ctor 本质上是线程安全的 (CLR),类似于静态字段初始值设定项吗?

【讨论】:

  • 是的。静态字段初始值设定项被编译到静态 ctor 中。
猜你喜欢
  • 2013-03-04
  • 2018-05-15
  • 1970-01-01
  • 2020-01-06
  • 1970-01-01
  • 1970-01-01
  • 2013-10-03
相关资源
最近更新 更多