如何在C#中的泛型类型参数中使用Switch ... Case?

时间:2018-04-10 16:17:00

标签: c#

我有一个类,我使用Generic Type Parameter来动态使用它。现在,我正在使用if..else,它适用于我的自定义类。我想知道我是否可以在这里使用switch..case

如果数据类型为decimalint,那么我可以像这样使用TypeCode

switch (Type.GetTypeCode(typeof(T)))
{
    case TypeCode.Int32:
       break;
    case TypeCode.Decimal:
       break;
}

但我有我创建的自定义类,我想使用它们。如果可能,下面的代码可以使用switch..case

我的代码:

void LogError<T>(T response, string orderId)
{
    if (typeof(T) == typeof(QuoteResponse))
    {
        var quoteResponse = response as QuoteResponse;

        //Do something
    }
    else if (typeof(T) == typeof(CommitResponse))
    {
        var commitResponse = response as CommitResponse;

        //Do something
    }
    else if (typeof(T) == typeof(CancelResponse))
    {
        var cancelResponse = response as CancelResponse;

        //Do something
    }
    else if (typeof(T) == typeof(StatusResponse))
    {
        var statusResponse = response as StatusResponse;

        //Do something
    }
}

3 个答案:

答案 0 :(得分:6)

如果您需要知道T的类型,您可能没有正确使用泛型。问问自己:如果有人使用您未考虑的T会发生什么?

看起来你需要的是拥有一个基类Response,然后从中派生其他类,然后你可以创建一个工厂方法,根据一些逻辑产生派生类的相应实例。

答案 1 :(得分:1)

C#switch关于通用类型参数 i.e。 <T>


的语句

0。下面的代码中使用的演示类型。

class MyClass { };    struct MyStruct { };    enum MyEnum { };

1。。在您的项目的全局范围内,使用单个通用类型参数定义一个struct,如下所示。

public struct TokenOf<X> { };

3。 switch语句。该演示演示了通用的方法,但是当然,该技术也可以在通用classstruct范围内的任何地方使用:(请注意,此方法使用{{ 3}}语法在 C#8 中可用):

string GenericMethod<T>() =>
    default(TokenOf<T>) switch
    {
        TokenOf<int> _      /**/ => "II",
        TokenOf<Guid> _     /**/ => "GG",
        TokenOf<string> _   /**/ => "SS",
        TokenOf<object> _   /**/ => "OO",
        TokenOf<MyEnum> _   /**/ => "EE",
        TokenOf<MyClass> _  /**/ => "CC",
        TokenOf<MyStruct> _ /**/ => "QQ",
        _                   /**/ => "??",
    };

3a。。在这些开关表达式示例中,可以使用{ }代替“丢弃符号” _以获得等同的结果。这建议使用可爱的(或可能是侮辱性的)代码格式化选项,例如:

string GenericMethod<T>() => default(TokenOf<T>) switch
{
    TokenOf<int>      /**/ _ => "II",
    TokenOf<Guid>     /**/ _ => "GG",
    // etc...
    TokenOf<MyStruct> /**/ _ => "QQ",
    {                 /**/ } => "??",
};

4。演示测试代码

void switch_on_generic_type() =>
    Console.WriteLine($@"{
        GenericMethod<int>()}   {
        GenericMethod<Guid>()}   {
        GenericMethod<string>()}   {
        GenericMethod<object>()}   {
        GenericMethod<MyEnum>()}   {
        GenericMethod<MyClass>()}   {
        GenericMethod<MyStruct>()}   {
        GenericMethod<double>()}");
    }

5。演示的输出

II GG SS OO EE CC QQ ??


6。讨论

该技术利用了一个事实:.NET值类型永远不会被实例化。随之而来的是,值类型(与引用类型不同,例如引用类型,如果System.Type存储在未指定的变量中,则它们会丢失其null身份)永远不会丢失其特定身份。因此,即使所有都是“默认”实例(实际上大小为1个字节),也可以保证TokenOf<>结构为System.Type的每个不同参数化表示不同的X标识)。 struct的泛型类型参数对于区分它们是必要且足够的。

要清楚一点,请注意,尽管TokenOf<>因此必须struct,但接通的泛型类型参数<X>可以是任何有效的.NET。类型。实际上,如果保证X代表值类型, 您甚至根本不需要TokenOf<>包装器 。例如,考虑一个类型实参T受关键字unmanaged(或struct)约束的泛型类或方法。在这种情况下,上面的示例(3.)可以简化为:

string GenericMethod<T>() where T : unmanaged =>
    default(T) switch
    {
        int _      /**/ => "II",
        Guid _     /**/ => "GG",
        MyEnum _   /**/ => "EE",
        MyStruct _ /**/ => "QQ",
        TimeSpan _ /**/ => "TS",
        _          /**/ => "??",
    };

此页面上的其他答案中使用了typeof(T)运算符,但是因为它(根据定义)在T上表现出switch expression运算,该运算在本质上对静态分析是不透明的,并且本质上是存在的在reification实例上硬阻塞(它本身显然只能在运行时存在),我认为它是一个非常钝和戏剧性的工具。

通过避免使用typeof,此处显示的技术可能比编译器,优化器,分析器和其他System.Type CTS系统更友好(正在研究中)。最重要的是,放松了过多强制System.Type实例化(通过typeof进行实例化的CIL机制)可能对JIT有利。

答案 2 :(得分:0)

这实际上是一个糟糕的设计,因为泛型方法应该是泛型

如果你真的需要它,你可以使用模式匹配来使代码简洁 -

switch (response)
{
    case QuoteResponse q:
        // do something
    case CommitResponse ct:
        // do something else
    ...
}