在你批评并指出C# specification的§8.7.2之前,请仔细阅读:)
我们都知道C#中的switch是怎样的。好的,请考虑使用“讨厌的” Bar 方法
的班级MainWindow
static int barCounter = 0;
public static int Bar()
{
return ++barCounter;
}
在这个课程的某个地方我们有这样的代码
Action switchCode = () =>
{
switch (Bar())
{
case 1:
Console.WriteLine("First");
break;
case 2:
Console.WriteLine("Second");
break;
}
};
switchCode();
switchCode();
在控制台窗口中,我们将看到
First
Second
在C#中使用表达式我们也可以这样做 - 编写相同的代码
var switchValue = Expression.Call(typeof(MainWindow).GetMethod("Bar"));
var WriteLine = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) });
var @switch = Expression.Switch(switchValue,
Expression.SwitchCase(
Expression.Call(WriteLine, Expression.Constant("First")),
Expression.Constant(1)
),
Expression.SwitchCase(
Expression.Call(WriteLine, Expression.Constant("Second")),
Expression.Constant(2)
)
);
Action switchCode = Expression.Lambda<Action>(@switch).Compile();
switchCode();
switchCode();
在DebugView中,我们可以看到“这个表达式背后的代码”
.Switch (.Call WpfApplication1.MainWindow.Bar()) {
.Case (1):
.Call System.Console.WriteLine("First")
.Case (2):
.Call System.Console.WriteLine("Second")
}
嗯,如果我们使用Expression.Call
代替Expression.Constant
怎么办?
public static bool foo1() { return false; }
public static bool foo2() { return true; }
// .....
var foo1 = Ex.Call(typeof(MainWindow).GetMethod("foo1"));
var foo2 = Ex.Call(typeof(MainWindow).GetMethod("foo2"));
var switchValue = Ex.Call(typeof(MainWindow).GetMethod("Bar"));
var WriteLine = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) });
var @switch = Ex.Switch(Ex.Constant(true),
Ex.SwitchCase(
Ex.Call(WriteLine, Ex.Constant("First")),
foo1
),
Ex.SwitchCase(
Ex.Call(WriteLine, Ex.Constant("OK!")),
Ex.Equal(switchValue, Ex.Constant(2))
),
Ex.SwitchCase(
Ex.Call(WriteLine, Ex.Constant("Second")),
foo2
)
);
Action switchCode = Ex.Lambda<Action>(@switch).Compile();
switchCode();
switchCode();
控制台窗口显示,正如我们预期的那样
Second
OK!
和DebugView
.Switch (True) {
.Case (.Call WpfApplication1.MainWindow.foo1()):
.Call System.Console.WriteLine("First")
.Case (.Call WpfApplication1.MainWindow.Bar() == 2):
.Call System.Console.WriteLine("OK!")
.Case (.Call WpfApplication1.MainWindow.foo2()):
.Call System.Console.WriteLine("Second")
}
因此可以在case语句中使用非常量表达式:)
好的,我知道这是一个很“混乱”的代码。但这是我的问题(最后:P):
有没有办法扩展IDE / VisualStudio /编译器的功能来实现这一点,但有更优雅的代码?
像这样的东西
switch (true)
{
case foo1():
Console.WriteLine("First");
break;
case Bar() == 2:
Console.WriteLine("OK!");
break;
case foo2():
Console.WriteLine("Second");
break;
}
我知道这将是一些扩展,代码将不一样(性能不一样)。但我想知道这是否有可能即时“改变”代码 - 就像匿名函数或yield return一样转换为嵌套类。
我希望有人通过上述文字并留下一些线索。
答案 0 :(得分:2)
一般来说,据我所知,Microsoft C#编译器中没有扩展点(甚至Roslyn也不打算改变它)。但是没有什么可以阻止你编写自己的C#编译器,或者更实际地,修改开源Mono C# compiler。
无论如何,我认为这比它的价值要麻烦得多。
但也许你可以用语言中的东西,即lambdas和方法调用来做你想做的事情,以形成一个“流畅的开关”:
Switch.Value(true)
.Case(() => Foo(), () => Console.WriteLine("foo"))
.Case(() => Bar() == 2, () => Console.WriteLine("bar == 2"));
如果您不介意每次都会评估所有条件值,那么您可以简化一下:
Switch.Value(true)
.Case(Foo(), () => Console.WriteLine("foo"))
.Case(Bar() == 2, () => Console.WriteLine("bar == 2"));
答案 1 :(得分:1)
不,这是不可能的,也不是我所知道的。它已经是奇迹,您可以在string
内部使用switch
语句(具有不可变行为的引用类型)。对于这类情况,只需使用if
,if/else
,if/elseif
组合。
答案 2 :(得分:1)
目前没有扩展程序可以执行此类操作。虽然值得指出的是MS SQL可以准确地提供您正在寻找的内容
SELECT
Column1, Column2,
CASE
WHEN SomeCondition THEN Column3
WHEN SomeOtherCondition THEN Column4
END AS CustomColumn
FROM ...
这个问题变成了理解的优先顺序;当两个条件都成立时会发生什么?在SQL中,case语句返回第一个语句为true的值,并忽略其他情况,但该行为可能不是您想要的。
C#的优势在于不可能以这样的方式对开关进行编码,即情况1和情况2都可以同时为真,因此只保证一个正确的答案。
答案 3 :(得分:0)
众所周知,“开关”公开了与if
类似的功能。我们大多数人(我自己包括)将其视为语法糖 - 在某个switch
上阅读大量案例更容易,然后通过if / else if / else if / else读取。但事实是,切换不是语法糖。
您必须意识到,为switch
(无论是IL还是机器代码)生成的代码与为顺序ifs生成的代码不同。 Switch有一个很好的优化,正如@Ed S.已经指出的那样,它能够在一个恒定的时间内运行。