【发布时间】:2012-04-10 09:53:58
【问题描述】:
在你开始批评和指点我C# specification 的§8.7.2 之前,请仔细阅读:)
我们都知道开关在 C# 中的样子。好的,所以考虑使用“讨厌” 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!
和调试视图
.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-statement 中使用非常量表达式 :)
好的,我知道这是一些“杂乱无章”的代码。但我的问题来了(最后: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 被转换为嵌套类。
我希望有人通过上面的文字并留下一些线索。
【问题讨论】:
-
似乎要付出很多努力才能解决一个不存在的问题。开关可以提供恒定的查找时间。在您有大量可能值的情况下,这是一件好事。只需使用一系列
if/else if并继续解决实际问题(或使用一种更注重表现力而非全面性能的语言)。 -
不,我认为你是在浪费时间,而不是用它来解决实际问题。是的,有些语言可以做你建议的事情,它们也不能提供恒定的时间查找。你这一生的时间是有限的,我的建议是不要把它浪费在这样对任何人都没有明显好处的事情上。编程语言只是一种工具,而不是其自身的目标。至于你的其他问题......不,谢谢,我对 UI 怪癖不感兴趣,这就是为什么我不以构建 UI 为生:)
-
@EdS.:我不同意花时间思考编程语言是浪费时间。可以对代码执行许多有趣的转换,即使是那些没有转化为有用的东西的转换也是有价值的。这就是我们进行研究的原因。更具体地说,我注意到您说开关 can 提供恒定时间行为是正确的。我注意到他们没有保证这样做;实际上,可以使用对数或线性算法生成开关。
-
@EricLippert:当然,总的来说我同意,只是在这种情况下不同意。我实际上要提到的是,尽管我认为这是一个试图找到问题的解决方案,但调查 C# 扩展的行为通常是一个有趣的行为。是的...一定要说“可以”:)
-
我的问题是,这并没有解决任何问题,它没有给我们提供一种方法来做我们以前做不到的事情。
标签: c# .net compiler-construction expression switch-statement