【问题标题】:C# generics: any way to refer to the generic parameter types as a collection?C# 泛型:有什么方法可以将泛型参数类型称为集合?
【发布时间】:2013-08-04 16:38:32
【问题描述】:

我需要写一堆接受1..N个泛型类型参数的方法,例如:

int Foo<T1>();
int Foo<T1,T2>();
int Foo<T1,T2,T3>();
...
int Foo<T1,T2,T3...TN>();

Foo()里面我想为每种类型做一些事情,例如

int Foo<T1,T2,T3>() {
    this.data = new byte[3]; // allocate 1 array slot per type
}

有什么方法可以参数化它,这样我就不会编辑Foo() 的每个变体,类似于:

int Foo<T1,T2,T3>() {
    this.data = new byte[_NUMBER_OF_GENERIC_PARAMETERS];
}

理想情况下,我还希望能够获得类型的数组或集合:

int Foo<T1,T2,T3>() {
    this.data = new byte[_NUMBER_OF_GENERIC_PARAMETERS];

    // can do this
    Type [] types = new Type[] { T1, T2, T3 };
    // but would rather do this
    Type [] types = _ARRAY_OR_COLLECTION_OF_THE_GENERIC_PARAMETERS;
}

【问题讨论】:

  • 在 C# 类型系统的上下文中,拥有可变参数泛型参数列表实际上没有任何意义——您将如何在其签名中使用这些类型创建一个方法?如果你从不在方法签名中使用类型参数,那么直接使用Types 的集合会更合适。 (除非您尝试捕获可变参数列表的编译时类型,但这也没有任何意义 - params 列表是一个预定类型的数组。)
  • 看看这个,stackoverflow.com/questions/44153/…,但是我怀疑这比它的价值更麻烦。
  • 看看 Jon Skeet 在stackoverflow.com/questions/213333/… 的回答。您可以根据需要使用 params 关键字。

标签: c# .net generics


【解决方案1】:

您可以从MethodInfo.GetGenericArguments array 中读取当前的泛型参数及其编号。

您可以使用MethodBase.GetCurrentMethod method 为您当前的方法检索MethodInfo

请注意,由于 C# 和 CLI 不支持可变泛型参数列表,因此您仍需要为方法的多个泛型重载提供不同数量的泛型参数。

因此,具有三个泛型参数的方法的代码示例可以这样编写:

int Foo<T1,T2,T3>() {
    MethodInfo mInfo = (MethodInfo)MethodBase.GetCurrentMethod();
    Type[] types = mInfo.GetGenericArguments();

    this.data = new byte[types.Length];
}

【讨论】:

  • 谢谢先生! (我认为您的回复中有错字-应该是 GetGenericArguments() 而不是 GenericTypeArguments())
  • @CoderBrien:你是对的 - GenericTypeArguments 存在于 Type 类中。已更正。
  • 我知道您正在尝试避免设置 typesdata “静态”,即使用您在编译时拥有的信息,但请注意,这种方法会将负担转移到运行 -时间;也就是说,这种使用反射的动态方法具有静态方法所没有的性能影响(因为编译器会完成所有这些工作)。
  • 我理解反射的性能含义。这对我的用例来说不是问题。我并不想避免使用编译时间信息。我正在尝试最小化我的 1..N 复制方法的代码维护。
【解决方案2】:

1) 您可以通过反射获取模板参数的数量:http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx。这样,您可以为每个 Foo.在每个 Foo 中,您只需调用:

FooImpl();

唯一的区别(关于“GetCurrentMethod”)是您需要获取前一个方法的方法信息:

StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();

2) 您可以在运行时生成所有 Foo 版本 - 所有版本都将共享仅调用 FooImpl 的相同实现。在运行时生成方法的一些细节:Creating a function dynamically at run-time 和这里:http://msdn.microsoft.com/en-us/library/exczf7b9.aspx

【讨论】:

    【解决方案3】:

    .NET 框架将具有 N 个类型参数的泛型类或方法视为具有不同于具有更多或更少类型参数的名称。如果不对框架进行重大更改来安排事物以便调用函数,这可能是可能的

    foo<T>(autogeneric ref T it)
    

    作为:

    foo(1, "George", 5.7);
    

    会被翻译成:

    struct foo99 {public int p1; public string p2; public double p3};
    ...
    foo99 temp99;
    temp99.p1 = 1;
    temp99.p2 = "George";
    temp99.p3 = 5.7;
    foo1(ref temp);
    

    这将允许泛型方法有效地接受任意数量的参数。能够通过 ref 传递这种匿名结构可能看起来不是很有用,但与 lambdas 结合使用时会非常强大。

    通常,关闭 lambda 表达式中的局部变量需要将局部变量提升到堆对象并构建以该堆对象为目标的委托。如果可以使用上述样式,则可以不创建持久闭包对象和委托并传递委托,而只需将适当的变量提升到结构并将 byref 与静态委托一起传递给它。这仅适用于被调用例程不必持久化传入的闭包的情况,但另一方面,调用者会知道被调用例程不会持久化闭包。此外,虽然没有 .NET 语言会支持这样的事情,并且可能需要一些框架更改以允许执行此操作的代码是可变的,但通过引用传递一个结构,其中一些成员也是 byrefs 可以使 lambda 访问封闭过程的ref 参数——这是目前不可能的事情(因为不能保证创建的委托不会超过它被创建的范围)。结构会在它们所在的作用域消亡时消亡,因此问题不必与它们一起存在。

    我希望 .NET 语言能够方便地表达这些概念。闭包会导致变量的生命周期被任意持久化这一事实意味着曾经在闭包中使用过变量的代码必须假设外部代码可以随时更改它。基于结构的方法不会有这个问题。

    【讨论】:

      【解决方案4】:

      没有。 您不能随意使用 Type 参数。但是你可以使用Tuple 之类的东西。它允许您包装泛型。但是你不能使用 TypeParamter 本身。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-06
        • 1970-01-01
        相关资源
        最近更新 更多