使用非常量表达式切换语句 - 扩展C#/ IDE能力

Car*_*fex 5 .net c# compiler-construction expression switch-statement

在你开始批评并指出我的C#规范的 §8.7.2之前,请仔细阅读:)

我们都知道C#中的switch是怎样的.好的,请考虑MainWindow使用"讨厌的" Bar方法

static int barCounter = 0;
public static int Bar()
{
    return ++barCounter;
}
Run Code Online (Sandbox Code Playgroud)

在这个类的某个地方,我们有这样的代码

Action switchCode = () =>
{
    switch (Bar())
    {
        case 1:
            Console.WriteLine("First");
            break;
        case 2:
            Console.WriteLine("Second");
            break;
    }
};

switchCode();

switchCode();
Run Code Online (Sandbox Code Playgroud)

在控制台窗口中,我们将看到

First
Second
Run Code Online (Sandbox Code Playgroud)

在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();
Run Code Online (Sandbox Code Playgroud)

在DebugView中,我们可以看到这个表达式的"代码隐藏"

.Switch (.Call WpfApplication1.MainWindow.Bar()) {
.Case (1):
        .Call System.Console.WriteLine("First")
.Case (2):
        .Call System.Console.WriteLine("Second")
}
Run Code Online (Sandbox Code Playgroud)

嗯,如果我们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();
Run Code Online (Sandbox Code Playgroud)

控制台窗口显示,正如我们所料

Second
OK!
Run Code Online (Sandbox Code Playgroud)

和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")
}
Run Code Online (Sandbox Code Playgroud)

因此可以在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;
}
Run Code Online (Sandbox Code Playgroud)

我知道这将是一些扩展,代码将不一样(性能不一样).但我想知道这是否有可能即时"改变"代码 - 就像匿名函数或yield return一样转换为嵌套类.

我希望有人通过上述文字并留下一些线索.

Tig*_*ran 1

不,这是不可能的,我也不知道。可以使用inside语句(具有不可变行为的引用类型)已经是一个奇迹。对于此类情况,只需使用, ,组合。stringswitchifif/elseif/elseif

  • 虽然我完全同意你的答案,但我不知道为什么在 switch 中使用字符串是一个奇迹,因为你实际使用的是编译时常量(文字字符串值)。 (2认同)