鉴于c#不能切换类型(我收集到没有作为特殊情况添加,因为关系意味着可能应用多个不同的情况),有没有更好的方法来模拟类型切换?
void Foo(object o)
{
if (o is A)
{
((A)o).Hop();
}
else if (o is B)
{
((B)o).Skip();
}
else
{
throw new ArgumentException("Unexpected type: " + o.GetType());
}
}
c#中绝对没有类型切换(更新:在c# 7 / VS 2017中支持类型切换-参见Zachary Yates的回答)。为了不使用大量的if/else if/else语句,您需要使用不同的结构。前段时间我写了一篇博文,详细介绍了如何构建TypeSwitch结构。
https://learn.microsoft.com/archive/blogs/jaredpar/switching-on-types
短版本:TypeSwitch的设计目的是防止冗余强制转换,并提供类似于普通switch/case语句的语法。例如,下面是TypeSwitch在一个标准Windows窗体事件上的操作
TypeSwitch.Do(
sender,
TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));
TypeSwitch的代码实际上非常小,可以很容易地放入你的项目中。
static class TypeSwitch {
public class CaseInfo {
public bool IsDefault { get; set; }
public Type Target { get; set; }
public Action<object> Action { get; set; }
}
public static void Do(object source, params CaseInfo[] cases) {
var type = source.GetType();
foreach (var entry in cases) {
if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
entry.Action(source);
break;
}
}
}
public static CaseInfo Case<T>(Action action) {
return new CaseInfo() {
Action = x => action(),
Target = typeof(T)
};
}
public static CaseInfo Case<T>(Action<T> action) {
return new CaseInfo() {
Action = (x) => action((T)x),
Target = typeof(T)
};
}
public static CaseInfo Default(Action action) {
return new CaseInfo() {
Action = x => action(),
IsDefault = true
};
}
}
我将创建一个接口,其名称和方法名称对您的交换机有意义,让我们分别调用它们:IDoable,告诉实现void Do()。
public interface IDoable
{
void Do();
}
public class A : IDoable
{
public void Hop()
{
// ...
}
public void Do()
{
Hop();
}
}
public class B : IDoable
{
public void Skip()
{
// ...
}
public void Do()
{
Skip();
}
}
修改方法如下:
void Foo<T>(T obj)
where T : IDoable
{
// ...
obj.Do();
// ...
}
至少这样你在编译时是安全的,而且我认为在性能方面它比在运行时检查类型要好。
您正在寻找的是歧视性联合,这是f#的一个语言特性,但您可以通过使用我制作的名为OneOf的库来实现类似的效果
https://github.com/mcintyre321/OneOf
与switch(以及if和异常作为控制流)相比,它的主要优点是它是编译时安全的——没有默认处理程序或漏洞
void Foo(OneOf<A, B> o)
{
o.Switch(
a => a.Hop(),
b => b.Skip()
);
}
如果你在o中添加第三个项,你会得到一个编译器错误,因为你必须在switch调用中添加一个处理程序Func。
你也可以执行.Match来返回一个值,而不是执行一个语句:
double Area(OneOf<Square, Circle> o)
{
return o.Match(
square => square.Length * square.Length,
circle => Math.PI * circle.Radius * circle.Radius
);
}