我有一个类,我使用Generic Type Parameter
来动态使用它。现在,我正在使用if..else
,它适用于我的自定义类。我想知道我是否可以在这里使用switch..case
。
如果数据类型为decimal
或int
,那么我可以像这样使用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
}
}
答案 0 :(得分:6)
如果您需要知道T
的类型,您可能没有正确使用泛型。问问自己:如果有人使用您未考虑的T
会发生什么?
看起来你需要的是拥有一个基类Response
,然后从中派生其他类,然后你可以创建一个工厂方法,根据一些逻辑产生派生类的相应实例。
答案 1 :(得分:1)
switch
关于通用类型参数 i.e。 <T>
0。下面的代码中使用的演示类型。
class MyClass { }; struct MyStruct { }; enum MyEnum { };
1。。在您的项目的全局范围内,使用单个通用类型参数定义一个struct
,如下所示。
public struct TokenOf<X> { };
3。 switch
语句。该演示演示了通用的方法,但是当然,该技术也可以在通用class
或struct
范围内的任何地方使用:(请注意,此方法使用{{ 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
...
}