Прежде чем вы начнете критиковать и указывать мне на §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 — это преобразование во вложенный класс.
Я надеюсь, что кто-нибудь прочтет приведенный выше текст и оставит какую-нибудь подсказку.