Reputation: 1222
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
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
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
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