Victor Trusov
Victor Trusov

Reputation: 1222

Switch expression on System.Type in C# 8

I'm curious is any other way to write something like this with new switch expression in C# 8?

public static object Convert(string str, Type type) =>
    type switch
    {
        _ when type == typeof(string) => str,
        _ when type == typeof(string[]) => str.Split(new[] { ',', ';' }),
        _ => TypeDescriptor.GetConverter(type).ConvertFromString(str)
    };

Because _ when type == typeof(string) looks kinda weird especially when we have type pattern and other very convenient instruments.

Upvotes: 10

Views: 9678

Answers (3)

Pasi Savolainen
Pasi Savolainen

Reputation: 2500

In a similar situation I ended up using a generic helper to make intent clearer. I did have <T> type available but not the C#9 default (T) switch { string[] => ...} syntax.

This solution seems to be handling nullables like DateTime? correctly.

static object Convert(string str, Type type)
    => type switch {
        _ when Match<string>(type) => str,
        _ when Match<string[]>(type) => str.Split(new[] { ',', ';' }),
        _ => TypeDescriptor.GetConverter(type).ConvertFromString(str)
    };
static bool Match<T>(Type t) => typeof(T) == t;

For my <T> available case it became something like:

static object Convert<T>(string str)
    => type switch
    {
        _ when Match<string, T>() => str,
        _ when Match<string[], T>() => str.Split(new[] { ',', ';' }),
        _ => TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(str)
    };
static bool Match<TA, TB>() => typeof(TA) == typeof(TB);

edit: One can shorten the syntax a bit with an extension method:

...
public static bool Is<T>(this System.Type type) => type == typeof(T);
...
static object Convert(string str, Type type)
    => type switch {
        _ when type.Is<string>() => str,
        _ when type.Is<string[]>() => str.Split(new[] { ',', ';' }),
        _ => TypeDescriptor.GetConverter(type).ConvertFromString(str)
    };

Upvotes: 0

CleanCoder
CleanCoder

Reputation: 2867

If you would use generics and use the latest c# version you could do something like this:


public partial class DateMultiSelect<TDate> where TDate: struct
{
    protected override void OnParametersSet()
    {
        if(default(TDate) switch 
        {
            DateOnly => true,
            DateTime => true,
            DateTimeOffset => true,
            _ => false
        })
            throw new ArgumentException($"TDate must be a date type, but is {typeof(TDate).Name}");

        base.OnParametersSet();
    }
}

according to your example this would be something along those lines: (can be compiled, but didn't test it at runtime)

public static T? Convert<T>(string? str) =>
        default(T) switch
        {
            string => (T?)(dynamic?)str,
            string[] => (T?)(dynamic?)str?.Split(new[] { ',', ';' }),
            _ => (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(str)
        };

instead of dynamic also object could be used.

Upvotes: 2

draconastar
draconastar

Reputation: 455

As others have alluded to, you actually need to have an instance of a type available to use the new type-matching features, not the representative System.Type. If you want to match directly on the type, the way you're doing it seems to be the only viable way for the time being.

That being said, I would argue in this case that a standard switch statement might be more readable:

switch (type)
{
    case Type _ when type == typeof(string):
        return str;

    case Type _ when type == typeof(string[]):
        return str.Split(',', ';');

    default:
        return TypeDescriptor.GetConverter(type).ConvertFromString(str);
}

If you really want to keep the switch expression, you could potentially get around this by matching on type names instead, though as a commenter pointed out below, this option is particularly brittle and will not work with certain types (such as DateTime? or Nullable<DateTime>):

public static object Convert(string str, Type type) =>
    type.Name switch
    {
        nameof(string) => str,
        nameof(string[]) => str.Split(new[] { ',', ';' }),
        _ => TypeDescriptor.GetConverter(type).ConvertFromString(str)
    };

Upvotes: 4

Related Questions