c# - 带有非常量表达式的 Switch 语句 - 扩展 C#/IDE 能力

标签 c# .net compiler-construction expression switch-statement

在你开始批评和指点我之前 C# specification 的§8.7.2| ,仔细阅读:)

我们都知道 switch 在 C# 中的样子。好的,那么考虑类 MainWindow 和“讨厌的”Bar 方法

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!

和调试 View

.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 转换为嵌套类。

希望有人看完上面的文字,留下一些线索。

最佳答案

一般来说,据我所知,Microsoft C# 编译器中没有扩展点(甚至 Roslyn 也不打算更改它)。但没有什么能阻止您编写自己的 C# 编译器,或者更现实地说,修改开源 Mono C# compiler .

无论如何,我认为这比它的值(value)更麻烦。

但也许您可以使用语言中已有的东西(即 lambda 和方法调用)来做您想做的事,以形成“流畅的开关”:

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"));

关于c# - 带有非常量表达式的 Switch 语句 - 扩展 C#/IDE 能力,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9879849/

相关文章:

c# - 一年范围的 2 个下拉列表

c# - 为什么我们可以使用这样的任务?

c++ - Visual Studio 中的 _ITERATOR_DEBUG_LEVEL 错误

c# - 当 C# 在同一个包含类中时,为什么以及如何允许访问类本身外部的私有(private)变量?

assembly - 是否有任何语言/编译器使用具有非零嵌套级别的 x86 ENTER 指令?

c# - 一个 DNN 模块中的异常阻止处理页面上的所有其他模块

c# - 使用 MD5.Create 和 MD5CryptoServiceProvider 有什么区别?

c# - 从 ClaimsIdentity 检索 WindowsIdentity 的最佳方法是什么

.net - XMLWriter 与 XMLDictionaryWriter

c# - 如何对字典中的所有项目求平均值并按键求平均值