Before you start criticizing and pointing me §8.7.2 of C# specification, read carefully :)
We all know how switch looks like in C#. Ok so consider the class MainWindow
with "nasty" Bar method
static int barCounter = 0;
public static int Bar()
{
return ++barCounter;
}
Somewhere in this class we have code like this
Action switchCode = () =>
{
switch (Bar())
{
case 1:
Console.WriteLine("First");
break;
case 2:
Console.WriteLine("Second");
break;
}
};
switchCode();
switchCode();
In the console window we will see
First
Second
Using Expressions in C# we could do the same – writing much same code
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();
In DebugView we could see "code behind" this expression
.Switch (.Call WpfApplication1.MainWindow.Bar()) {
.Case (1):
.Call System.Console.WriteLine("First")
.Case (2):
.Call System.Console.WriteLine("Second")
}
Ehmm, what if we use Expression.Call
instead 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();
Console window shows, as we expected
Second
OK!
And 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")
}
So it is possible to use non-constant expression in case-statement :)
Ok, I known this is little ‘messy’ code. But here comes my question (finally :P):
Is there any way to extends functionality of IDE/VisualStudio/compiler to do this, but with more elegant code?
Something like this
switch (true)
{
case foo1():
Console.WriteLine("First");
break;
case Bar() == 2:
Console.WriteLine("OK!");
break;
case foo2():
Console.WriteLine("Second");
break;
}
I know this will be some extension and code will be not the same (not the same performance). But I wonder if this is even possible to "change" code on-the-fly – like anonymous function or yield return is transform to nested class.
I hope someone goes through the above text and leave some clue.
In general, there are no extension points in the Microsoft C# compiler as far as I know (and even Roslyn isn't planning to change that). But nothing is stopping you from writing your own C# compiler, or, more realistically, modifying the open source Mono C# compiler.
In any case, I think it's much more trouble than what it's worth.
But maybe you can do what you want using something that's already in the language, namely, lambdas and method calls, to form a “fluent switch”:
Switch.Value(true)
.Case(() => Foo(), () => Console.WriteLine("foo"))
.Case(() => Bar() == 2, () => Console.WriteLine("bar == 2"));
If you don't mind that all the condition values will be evaluated every time, you simplify that a bit:
Switch.Value(true)
.Case(Foo(), () => Console.WriteLine("foo"))
.Case(Bar() == 2, () => Console.WriteLine("bar == 2"));
No, it's not possible, nor that I'm aware of. It's already kind of miracle that you can use a string
inside switch
statement (reference type with immutable behavior). For these kind of cases just use if
, if/else
, if/elseif
combinations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With