114

I get a big if/else on class type. Is there a way to do it with a switch case?

Example:

function test(object obj) { if(obj is WebControl) { }else if(obj is TextBox) { } else if(obj is ComboBox) { } 

etc ...

I would like to create something like

switch(obj) { case is TextBox: break; case is ComboBox: break; } 
5

5 Answers 5

184

Update C# 7

Yes: Source

switch(shape) { case Circle c: WriteLine($"circle with radius {c.Radius}"); break; case Rectangle s when (s.Length == s.Height): WriteLine($"{s.Length} x {s.Height} square"); break; case Rectangle r: WriteLine($"{r.Length} x {r.Height} rectangle"); break; default: WriteLine("<unknown shape>"); break; case null: throw new ArgumentNullException(nameof(shape)); } 

Prior to C# 7

No.

http://blogs.msdn.com/b/peterhal/archive/2005/07/05/435760.aspx

We get a lot of requests for addditions to the C# language and today I'm going to talk about one of the more common ones - switch on type. Switch on type looks like a pretty useful and straightforward feature: Add a switch-like construct which switches on the type of the expression, rather than the value. This might look something like this:

switch typeof(e) { case int: ... break; case string: ... break; case double: ... break; default: ... break; } 

This kind of statement would be extremely useful for adding virtual method like dispatch over a disjoint type hierarchy, or over a type hierarchy containing types that you don't own. Seeing an example like this, you could easily conclude that the feature would be straightforward and useful. It might even get you thinking "Why don't those #*&%$ lazy C# language designers just make my life easier and add this simple, timesaving language feature?"

Unfortunately, like many 'simple' language features, type switch is not as simple as it first appears. The troubles start when you look at a more significant, and no less important, example like this:

class C {} interface I {} class D : C, I {} switch typeof(e) { case C: … break; case I: … break; default: … break; } 

Link: https://blogs.msdn.microsoft.com/peterhal/2005/07/05/many-questions-switch-on-type/

Sign up to request clarification or add additional context in comments.

12 Comments

I disagree with the language designers. Lots of languages have type switches. Anyway while there is no type-switch it is easy enough to implement see stackoverflow.com/questions/7252186/switch-case-on-type-c/…
Where's the rest of the answer? it now ends on "... , example like this:"
I don't care what he says. It is 'simple'. Rather it would be if they designed C# better from the start. I hear there are 10+ passes for all the grammar :(. Switch is essentially obj.member. The vtable is an inaccessible member. If its treated as a value (like int) you can use it.
@oɔɯǝɹ I supplied the link to the source of my information if you want to read the entire article. I wasn't going to paste the entire quote when you can read it at the source. The simple answer was "No".
UPDATE for anyone landing on this page: The answer is yes since C# 7. You can totally write switch cases over types now. See: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
|
61

The following code works more or less as one would expect a type-switch that only looks at the actual type (e.g. what is returned by GetType()).

public static void TestTypeSwitch() { var ts = new TypeSwitch() .Case((int x) => Console.WriteLine("int")) .Case((bool x) => Console.WriteLine("bool")) .Case((string x) => Console.WriteLine("string")); ts.Switch(42); ts.Switch(false); ts.Switch("hello"); } 

Here is the machinery required to make it work.

public class TypeSwitch { Dictionary<Type, Action<object>> matches = new Dictionary<Type, Action<object>>(); public TypeSwitch Case<T>(Action<T> action) { matches.Add(typeof(T), (x) => action((T)x)); return this; } public void Switch(object x) { matches[x.GetType()](x); } } 

6 Comments

Very interesting solution... in some ways this is terrible :) ... but in some ways this is incredible (especially in the event that some other external developer could tap into this system by creating class "X" and then supplying the 'what to do with X' logic ... sorta like a mini DI/Ioc)
When "in some ways this is terrible"?
@cdiggins, is there a way I can add something like "default" or "none of them" ?
@cdiggins though this is a clever camouflage, but no better than using if , else if, as it would behave when it would sequentially traverse the Case statements
@Pedro77 Yes you can check the existence of the Key in the Switch method and if it doesn't exist, then use the typeof(Object) for the default case in the end of Case chain
|
27

Yes, you can switch on the name...

switch (obj.GetType().Name) { case "TextBox":... } 

9 Comments

FYI, this solution won't work when given a public class MyCustomTextBox : TextBox
You would just do case "TextBox: "MyCustomTextBox": ...
Assuming you know all possible subclasses, sure.
I imagine that the person writing this code is writing it for a reason... and knows all of the subclasses :P - Example, he's probably trying to add a CssClass name for all "textboxes" and then all "datepickers" or whatever.
I know this is old, but I like your answer, and I agree with your reasoning that the person writing this would know all of their subclasses, as long as they're using the switch statement to specify action for specific classes, and not all children classes
|
16

Here's an option that stays as true I could make it to the OP's requirement to be able to switch on type. If you squint hard enough it almost looks like a real switch statement.

The calling code looks like this:

var @switch = this.Switch(new [] { this.Case<WebControl>(x => { /* WebControl code here */ }), this.Case<TextBox>(x => { /* TextBox code here */ }), this.Case<ComboBox>(x => { /* ComboBox code here */ }), }); @switch(obj); 

The x in each lambda above is strongly-typed. No casting required.

And to make this magic work you need these two methods:

private Action<object> Switch(params Func<object, Action>[] tests) { return o => { var @case = tests .Select(f => f(o)) .FirstOrDefault(a => a != null); if (@case != null) { @case(); } }; } private Func<object, Action> Case<T>(Action<T> action) { return o => o is T ? (Action)(() => action((T)o)) : (Action)null; } 

Almost brings tears to your eyes, right?

Nonetheless, it works. Enjoy.

5 Comments

+1 definitely clever. Not sure I'd use it in production code (not that there is anything wrong with it per-se).
The "new [] {" and "}" are superfluous.
Hehe, nice one. I agree with @Steve but +1
@cdiggins - I agree they are superflous, but I wrote the code to allow dropping the array initializer in favour of a list of parameters. The array initializer syntax is a little more readable, IMHO, once you have more than three parameters. And it looks more like the normal switch syntax - if you squint hard enough!
@Enigmativity loved the clever solution, but as I squinted hard enough found it, same as if, else if looping, not genuinely a switch case, this would again do the chaining of calls till it finds the first case which is not null. But agreed nice camouflage.
11

The simplest thing to do could be to use dynamics, i.e. you define the simple methods like in Yuval Peled answer:

void Test(WebControl c) { ... } void Test(ComboBox c) { ... } 

Then you cannot call directly Test(obj), because overload resolution is done at compile time. You have to assign your object to a dynamic and then call the Test method:

dynamic dynObj = obj; Test(dynObj); 

1 Comment

Cool solution! Got my vote :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.