在C#7.1中,以下是有效代码:
object o = new object();
switch (o)
{
case CustomerRequestBase c:
//do something
break;
}
然而,我想在以下场景中使用模式切换语句:
public T Process<T>(object message, IMessageFormatter messageFormatter)
where T : class, IStandardMessageModel, new()
{
switch (T)
{
case CustomerRequestBase c:
//do something
break;
}
}
IDE给了我一个错误"'T'是一种类型,在给定的上下文中无效"有没有一种优雅的方法可以打开泛型参数的类型?在我的第一个例子中,你打开了对象,第二个例子中我想打开类型T。做这件事的最佳方法是什么?
下面是两个不同的类,分别称为Foo和Bar。您可以使用其中任何类的一个实例作为名为Process的函数的参数。毕竟,您可以执行模式匹配,如示例函数中所示。有一个名为Test的函数作为用法示例。。
public class Foo
{
public string FooMsg { get; set; }
}
public class Bar
{
public string BarMsg { get; set; }
}
public class Example
{
public T Process<T>(T procClass) where T : class
{
switch (typeof(T))
{
case
var cls when cls == typeof(Foo):
{
var temp = (Foo)((object)procClass);
temp.FooMsg = "This is a Foo!";
break;
}
case
var cls when cls == typeof(Bar):
{
var temp = (Bar)((object)procClass);
temp.BarMsg = "This is a Bar!";
break;
}
}
return
procClass;
}
public void Test(string message)
{
Process(new Foo() { FooMsg = message });
Process(new Bar() { BarMsg = message });
}
}
我同意这种方法速度更快、不那么难看的情况,也同意在任何情况下都应该找到更好的解决方案,但有时这种权衡是不划算的。。。所以这里有一个解决方案(C#9.0(
return typeof(T) switch
{
Type t when t == typeof(CustomerRequestBase) => /*do something*/ ,
_ => throw new Exception("Nothing to do")
};
如果我们忽略根据注释进行的代码气味讨论,那么一个易于阅读的实现(破解(可能如下所示:
public T Process<T>(string number)
{
switch (typeof(T).FullName)
{
case "System.Int32":
return (dynamic) int.Parse(number);
case "System.Double":
return (dynamic) double.Parse(number);
default:
throw new ArgumentException($"{typeof(T).FullName} is not supported");
}
}
即使您使用了#通用约束,这也可能会导致问题,除非您是唯一的程序员;(
我要在前言中说,总的来说,我同意所有评论者的意见,即打开通用T可能不是一个好主意。在这种情况下,我建议他坚持识别对象,对其进行强制转换,然后将其传递给适当的处理程序。
然而,我一直在编写一个需要相当高效的自定义二进制序列化程序,我发现了一个案例,我觉得他要求的切换(或if语句(是合理的,所以下面是我如何管理它
public T ProcessAs<T>(parameters)
{
if (typeof(T) == typeof(your_type_here)
{
your_type_here tmp = process(parameters);
return Unsafe.As<your_type_here, T>(ref tmp);
}
else if (typeof(T) == typeof(your_other_type))
{
your_other_type tmp = otherProcess(parameters);
return Unsafe.As<your_other_type, T>(ref tmp);
}
else
{
throw new ArgumentException(appropriate_msg);
}
}
请注意,如果处理的是类而不是结构,则可以使用Unsafe.As<T>(T value)
。
我使用过类型的(T(、当T=typeof(X((T(的类型和typeof(T(.FullName变体(在其他答案中建议(,但我从未对它们中的任何一个感到满意。它们要么太复杂,要么太慢。
typeof(T(when T==typeof(X(可能是最好的,然而,它的性能值得怀疑,因为编译器似乎将when子句视为一系列if。。。否则如果。。。声明。运行调试器并跟踪它以了解我的意思。字符串变体也有类似的问题——获取类型名和比较字符串似乎是不必要的开销。
我们真正想要的是一种使用本机交换机哈希查找行为以获得最佳性能的解决方案。
因此,这里有另一个降低复杂性的解决方案:
- 创建一个伪变量T
- 指定新值
- 在switch语句中使用T类型的伪变量
结果:
public T Process<T>(object message, IMessageFormatter messageFormatter)
where T : class, IStandardMessageModel, new()
{
T dummy = Activator.CreateInstance(typeof(T));
switch (dummy)
{
case CustomerRequestBase _:
//do something
break;
}
}
T需要一个构造函数,在这种情况下,这是完美的,因为该方法是用限定的,其中T:class,new((是一个类并具有默认构造函数。因此,我们可以实例化伪变量,并使用该伪变量来执行标准的开关变量case Type功能。
警告:T dumm=default将不起作用,因为默认值通常为Null,并且我们不能在switch语句中使用Null。这就是使用Activator.CreateInstance((的原因。
Discard(_(in the case语句用于阻止使用伪变量。据推测,case语句将具有用于处理"消息"的其他逻辑。