简体   繁体   English

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

[英]How to use Switch…Case in generic type parameter in C#?

I have a class where I am using Generic Type Parameter to dynamically use it.我有一个类,我在其中使用Generic Type Parameter来动态使用它。 Right now, I am using if..else which works fine with my custom classes.现在,我正在使用if..else ,它适用于我的自定义类。 I was wondering if I can use switch..case here.我想知道我是否可以在这里使用switch..case

If data types are decimal or int then I can use TypeCode like this.如果数据类型是decimalint那么我可以像这样使用TypeCode

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

But I have custom classes which I created and I want to use them.但是我有我创建的自定义类,我想使用它们。 Below code works and trying to use switch..case if possible.下面的代码有效并尝试使用switch..case如果可能。

My Code:我的代码:

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
    }
}

C# switch statement on a generic type argument, ie <T>泛型类型参数上的 C# switch语句,<T>


0. Demonstration types used in the code below. 0.下面代码中使用的演示类型。

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

1. Globally in your project, define a struct with a single generic type argument as follows. 1.在您的项目中全局定义一个具有单个泛型类型参数的struct ,如下所示。

public struct TokenOf<X> { };

3. switch statement. 3. switch语句。 This demo shows a generic method , but of course the technique can also be used anywhere within the scope of a generic class or struct : (Note that this method uses switch expression syntax available in C# 8 ):这个演示展示了一个泛型方法,但当然,该技术也可以在泛型classstruct范围内的任何地方使用:(请注意,此方法使用C# 8 中可用的switch 表达式语法):

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. 3a. In these switch expression examples, you can use { } instead of the "discard symbol" _ for equivalent results.在这些 switch 表达式示例中,您可以使用{ }代替“丢弃符号” _以获得等效结果。 This suggests cute (or perhaps abusive) code formatting options such as:这暗示了可爱(或者可能是滥用)的代码格式选项,例如:

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

4. Demonstration test code 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. Output of the demo 5.演示的输出

II GG SS OO EE CC QQ ??


6. Discussion 6.讨论

This technique takes advantage of the fact that a .NET value-type can never not-be instantiated.这种技术利用了 .NET 值类型永远不能不被实例化的事实。 It follows that value-types (unlike as with reference types--which for example lose their System.Type identity if null is stored in an underspecified variable) can never lose their specific identity.因此,值类型(与引用类型不同——例如,如果null存储在未指定的变量中,则会丢失其System.Type标识)永远不会丢失其特定标识。 Therefore, the TokenOf<> struct is guaranteed to represent a different System.Type identity for each different parameterization by X , even when all are always "default" instances (which are actually 1-byte in size).因此, TokenOf<>结构保证为X每个不同参数化表示不同的System.Type标识,即使所有总是“默认”实例(实际上是 1 个字节的大小)。 The generic type argument of the struct is necessary and sufficient to differentiate them. struct的泛型类型参数对于区分它们是必要的和足够的。

To be clear, note that while TokenOf<> therefore must be a struct , the switched-upon generic type argument <X> can be any valid .NET type.需要明确的是,虽然TokenOf<>因此必须struct ,但切换的泛型类型参数<X>可以是任何有效的 .NET 类型。 And in fact, if X is guaranteed to represent a value-type, you don't even need the TokenOf<> wrapper at all .事实上,如果X保证代表一个值类型,你甚至根本不需要TokenOf<>包装器 For example, consider a generic class or method with a type argument T constrained by the unmanaged (or struct ) keyword.例如,考虑一个泛型类或方法,其类型参数Tunmanaged (或struct )关键字约束。 In this case, an example such as (3.) from above would reduce to this:在这种情况下,上面的 (3.) 等示例将简化为:

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

The typeof(T) operator is used in other answers on this page, but because it (by definition) manifests a reification operation on T that's both inherently opaque to static analysis, and existentially hard-blocked on a System.Type instance (which itself, obviously, can only exist at runtime) I consider it to be a pretty blunt and dramatic tool. typeof(T)运算符在本页的其他答案中使用,但因为它(根据定义)表现出T上的具体化操作,该操作本质上对静态分析不透明,并且在System.Type实例(本身,显然,只能在运行时存在)我认为它是一个非常直率和戏剧性的工具。

By avoiding typeof , the technique shown here may be friendlier to compilers, optimizers, analyzers, and other CTS offline-metadata systems (pending investigation).通过避免typeof ,此处显示的技术可能对编译器、优化器、分析器和其他CTS离线元数据系统(待调查)更友好。 Most-importantly, a CIL regime which is relaxed of excessive forcible System.Type instantiations (via typeof ) may prove favorable to the JIT.最重要的是,放松过度强制System.Type实例化(通过typeof )的 CIL 机制可能证明有利于 JIT。

If you need to know what the type of T is, you probably aren't using generics properly.如果您需要知道T的类型是什么,您可能没有正确使用泛型。 Ask yourself: what happens if someone uses a T that you haven't accounted for?问问自己:如果有人使用了您没有考虑过的T会怎样?

It seems like what you need is to have a base class Response and then derive the other classes from it, then you can create a factory method which produces the appropriate instances of the derived classes depending on some logic.看起来您需要的是拥有一个基类Response然后从中派生其他类,然后您可以创建一个工厂方法,该方法根据某些逻辑生成派生类的适当实例。

This is actually a bad design, because generic methods are supposed to be generic .这实际上是一个糟糕的设计,因为泛型方法应该是泛型的

In case you really need it though, you can use pattern matching to make the code concise --如果你真的需要它,你可以使用模式匹配来使代码简洁——

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM