Оператор Switch с непостоянным выражением — расширяет возможности C#/IDE

Прежде чем вы начнете критиковать и указывать мне на §8.7.2 спецификации 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(@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 .Константа?

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(@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 — это преобразование во вложенный класс.

Я надеюсь, что кто-нибудь прочтет приведенный выше текст и оставит какую-нибудь подсказку.

5
задан Andrew Savinykh 26 March 2012 в 21:16
поделиться