[英]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.如果数据类型是
decimal
或int
那么我可以像这样使用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
}
}
switch
statement on a generic type argument, ie <T>
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 ):这个演示展示了一个泛型方法,但当然,该技术也可以在泛型
class
或struct
范围内的任何地方使用:(请注意,此方法使用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.例如,考虑一个泛型类或方法,其类型参数
T
受unmanaged
(或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.