My context is that I'm building a simple factory method for creating instances of derived types of a given base type. The factory method only takes a type parameter, i.e. doesn't have any arguments. This is obviously possible with an if-else if construct:
public Vehicle Create<T>()
{
if (typeof(T) == typeof(Car))
return new Car(); // just an example; could be more complex logic.
else if (typeof(T) == typeof(Truck))
return new Truck(); // just an example; could be more complex logic.
else
throw new ArgumentException(
$"The type {typeof(T).Name} is not known by this method.");
}
It's well-known by now how to use pattern matching in C# (as of C# 7.0) to branch based on the type of a variable but this doesn't work for a type parameter:
switch (T) { ... } // error: "'T' is a type, which is not valid in the given context"
or...
switch (typeof(T))
{
case Car c: ...
// err: "An expression of type 'Type' cannot be handled by a pattern of type 'Car'"
}
So I'd like to know if it's possible to use switch to achieve the same result?
Research: I'm surprised this hasn't been asked before, but I can't find it. I found this post which has a name and a few answers that come pretty close but it's dealing with (numeric) value types and methods that have an argument of type T -- the generic type parameter. Similarly, this post also uses an argument.
I know your question specifically asked about using the switch statement, but another alternative would be to create a dictionary of factories keyed on the type.
You should note that at this point, you are doing an operation similar to dependency injection. You are asking for a Vehicle of type X and risking a run-time error if the Create method does not have the information needed to Create a Vehicle of type X.
public class Car : Vehicle { }
public class Truck : Vehicle { }
public abstract class Vehicle
{
private static readonly IReadOnlyDictionary<Type, Func<Vehicle>> vehicleFactories = new Dictionary<Type, Func<Vehicle>>
{
{ typeof(Car), () => new Car() },
{ typeof(Truck), () => new Truck() }
};
public static Vehicle Create<T>() where T : Vehicle, new()
{
if (vehicleFactories.TryGetValue(typeof(T), out var factory))
{
return factory();
}
else
{
throw new ArgumentException(
$"The type {typeof(T).Name} is not known by this method.");
}
}
}
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