Using Case/Switch and Gettype to Determine the Object

Using Case/Switch and GetType to determine the object

If I really had to switch on type of object, I'd use .ToString(). However, I would avoid it at all costs: IDictionary<Type, int> will do much better, visitor might be an overkill but otherwise it is still a perfectly fine solution.

How to use switch-case on a Type?

You cannot use a switch block to test values of type Type. Compiling your code should give you an error saying something like:

A switch expression or case label must be a bool, char, string,
integral, enum, or corresponding nullable type

You'll need to use if-else statements instead.

Also: typeof(int) and typeof(Int32) are equivalent. int is a keyword and Int32 is the type name.

UPDATE

If you expect that most types will be intrinsic you may improve performance by using a switch block with Type.GetTypeCode(...).

For example:

switch (Type.GetTypeCode(type))
{
case TypeCode.Int32:
// It's an int
break;

case TypeCode.String:
// It's a string
break;

// Other type code cases here...

default:
// Fallback to using if-else statements...
if (type == typeof(MyCoolType))
{
// ...
}
else if (type == typeof(MyOtherType))
{
// ...
} // etc...
}

Сasting a variable to a type without switch case


Pattern Matching

To start I want to present the use of pattern matching in switch statements to work with different types, as follows:

public static double ComputeAreaModernSwitch(object shape)
{
switch (shape)
{
case Square s:
return s.Side * s.Side;
case Circle c:
return c.Radius * c.Radius * Math.PI;
case Rectangle r:
return r.Height * r.Length;
default:
throw new ArgumentException(
message: "shape is not a recognized shape",
paramName: nameof(shape));
}
}

Example taken from Pattern Matching - C# Guide.


Type Dictionary

With that out of the way, yes, you can write a dictionary... the trouble will be on the type of the items.

We can do this:

Dictionary<Type, Action<object>> dictionary;

// (initialize and populate somewhere else) ...

if (dictionary.TryGetValue(element.GetType(), out var action))
{
action(element);
}

However, here you have to use Action<object> because we need to give a type to the items (and no, we can't say Action<?> - well, we can do Action<dynamic> but you cannot cast Action<someType> to Action<dynamic>), forcing you to cast inside the called method.

We can argue that a cast is a way to tell the compiler that we know something it does not. In this case that we know that that object is actually of a given type.

We could do a bit better/worse, depending on how you look at it...

Dictionary<Type, Delegate> dictionary;

// (initialize and populate somewhere else) ...

if (dictionary.TryGetValue(element.GetType(), out var @delegate))
{
@delegate.DynamicInvoke(element);
}

This is effectively late binding. We do not know the types at compile time... as developer you must ensure you provide a delegate of the correct type. However, if we are already enforcing knowledge that the compiler is unaware of, then this could be acceptable.

We can make a helper method to make it easier:

void SetMethod<T>(Action<T> action)
{
dictionary[typeof(T)] = action;
}

Here the compiler can check the type for the method is correct. Yet, from the point of view of the compiler this information is lost (not available) when you consume the dictionary. It is a kind of type erasure if you will.


Dynamic

Now, if we are forgoing types, we could use dynamic following good answer by TheGeneral.


Addendum: Calling a known method (with MethodInfo)

You can call a method by its name, for example, if you have the following:

class Helper
{
public static void Method(T input)
{
Console.WriteLine(input.GetType());
}
}

You can do this:

var methodInfo = typeof(Helper).GetMethod("Method");

// ...

methodInfo.Invoke(null, new object[]{element});

You could then put all your methods in a helper class, and find them by the name (which you could derive from the name of the type).


If you want to call a known method that has a generic parameter, you can use MethodInfo. We need to be aware of whatever or not the method is static, and whatever or not the generic argument is part of the method definition or the declaring type definition...

On one hand, if you have something like this:

class Helper<T>
{
public static void Method(T input)
{
Console.WriteLine(input.GetType());
}
}

You can do this:

var helperType = typeof(Helper<>);

// ...

var specificMethodInfo = helperType.MakeGenericType(element.GetType()).GetMethod("Method");
specificMethodInfo.Invoke(null, new object[]{element});

On the other hand, if you have this:

class Helper
{
public static void Method<T>(T input)
{
Console.WriteLine(input.GetType());
}
}

You can do this:

var methodInfo = typeof(Helper).GetMethod("Method");

// ...

var specificMethodInfo = methodInfo.MakeGenericMethod(element.GetType());
specificMethodInfo.Invoke(null, new object[]{element});

Note: I pass null as first parameter to invoke. That is the instance on which I am calling the method. None, because they are static. If they aren't then you need an instance... you could try creating one with Activator.CreateInstance, for example.


Addendum: Finding what to call (Type Discovery)

Perhaps you have disparate method to call (they are not the same but with different generic argument), but you do not want to have the trouble of populate the dictionary by hand.

That is where Type Discovery comes in.

To begin with, I suggest to use an attribute, for example:

[AttributeUsage(AttributeTargets.Method)]
public sealed class DataHandlerAttribute : Attribute { }

Then we need a list of the types where we will search. If we will search on a known assembly we could do this:

var assembly = typeof(KnownType).GetTypeInfo().Assembly;
var types = assembly.GetTypes();

Note: if your target platform does not support this (.NET Standard 1.0 to 1.4), you will have to hand code the list of types.

Next, we need a predicate to check if a given type is one of the ones in which we are interested:

bool IsDataHandlerMethod(MethodInfo methodInfo)
{
var dataHandlerAttributes = return (DataHandlerAttribute[])item.GetCustomAttributes(typeof(DataHandlerAttribute), true);
if (attributes == null || attributes.Length == 0)
{
return false;
}
if (methodInfo.DeclaringType != null)
{
return false;
}
if (methodInfo.ReturnTpye != typeof(void))
{
return false;
}
var parameters = methodInfo.GetParameters();
if (parameters.Length != 1)
{
return false;
}
if (paramters[0].IsByRef || paramters[0].IsOut)
{
return false;
}
return true;
}

And a method to convert them into delegates:

(Type, Delegate) GetTypeDelegatePair(MethodInfo methodInfo)
{
var parameters = methodInfo.GetParameters();
var parameterType = parameters[0].ParameterType;
var parameterTypeArray = new []{parameterType};
var delegateType = typeof(Action<>).MakeGenericType(parameterTypeArray);
var target = null;
if (!methodInfo.IsStatic)
{
var declaringType = methodInfo.DeclaringType;
target = instance = Activator.CreateInstance(declaringType);
}
return (parameterType, methodInfo.CreateDelegate(delegateType, target));
}

And now we can do this:

var dataHandlers = types
.SelectMany(t => t.GetTypeInfo().GetMethods())
.Where(IsDataHandlerMethod)
.Select(GetTypeDelegatePair);

And we will have an enumerable of pairs of types and delegate that we can use to populate our dictionary.

Note: the above code still needs some work (for example, could we just call GetParameters once?), and presumes a modern .NET target (extra work is needed to make it work in older platforms). Also notice the code for Type Discovery I present does not handle generic methods, you can check Type.IsGenericTypeDefinition and MethodInfo.IsGenericMethodDefinition... however, I would suggest to avoid them. In fact, it should be easy to modify for the case where you want to put all the methods in a single static class. You may also use a similar approach to get factory methods, for example.

Switch case on type c#

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/

C# switch on type

See another answer; this feature now exists in C#


I usually use a dictionary of types and delegates.

var @switch = new Dictionary<Type, Action> {
{ typeof(Type1), () => ... },
{ typeof(Type2), () => ... },
{ typeof(Type3), () => ... },
};

@switch[typeof(MyType)]();

It's a little less flexible as you can't fall through cases, continue etc. But I rarely do so anyway.

c# 7.0: switch on System.Type

The (already linked) new pattern matching feature allows this.

Ordinarily, you'd switch on a value:

switch (this.value) {
case int intValue:
this.value = Math.Max(Math.Min(intValue, Maximum), Minimum);
break;
case decimal decimalValue:
this.value = Math.Max(Math.Min(decimalValue, Maximum), Minimum);
break;
}

But you can use it to switch on a type, if all you have is a type:

switch (type) {
case Type intType when intType == typeof(int):
case Type decimalType when decimalType == typeof(decimal):
this.value = Math.Max(Math.Min(this.value, Maximum), Minimum);
break;
}

Note that this is not what the feature is intended for, it becomes less readable than a traditional if...else if...else if...else chain, and the traditional chain is what it compiles to anyway. I do not recommend using pattern matching like this.

Is there a better alternative than this to 'switch on type'?

Switching on types is definitely lacking in C# (UPDATE: in C#7 / VS 2017 switching on types is supported - see Zachary Yates's answer). In order to do this without a large if/else if/else statement, you'll need to work with a different structure. I wrote a blog post awhile back detailing how to build a TypeSwitch structure.

https://learn.microsoft.com/archive/blogs/jaredpar/switching-on-types

Short version: TypeSwitch is designed to prevent redundant casting and give a syntax that is similar to a normal switch/case statement. For example, here is TypeSwitch in action on a standard Windows form event

TypeSwitch.Do(
sender,
TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

The code for TypeSwitch is actually pretty small and can easily be put into your project.

static class TypeSwitch {
public class CaseInfo {
public bool IsDefault { get; set; }
public Type Target { get; set; }
public Action<object> Action { get; set; }
}

public static void Do(object source, params CaseInfo[] cases) {
var type = source.GetType();
foreach (var entry in cases) {
if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
entry.Action(source);
break;
}
}
}

public static CaseInfo Case<T>(Action action) {
return new CaseInfo() {
Action = x => action(),
Target = typeof(T)
};
}

public static CaseInfo Case<T>(Action<T> action) {
return new CaseInfo() {
Action = (x) => action((T)x),
Target = typeof(T)
};
}

public static CaseInfo Default(Action action) {
return new CaseInfo() {
Action = x => action(),
IsDefault = true
};
}
}

Select Case on an object's type in VB.NET

With VB 2010, for projects targeting .NET framework 4 and later, you can now do this:

Select Case msg.GetType()
Case GetType(ClassA)
End Select

In earlier VB versions, it didn't work because you couldn't compare two types with equality. You'd have to check if they point to the same reference using the Is keyword. It's not possible to do this in a Select Case, unless you use a property of the type like the Name or FullName for comparison, as suggested by Michael. You can use a combination of If and ElseIf though:

Dim type = msg.GetType()
If type Is GetType(ClassA)
...
ElseIf type Is GetType(ClassB)
...
...
End If


Related Topics



Leave a reply



Submit