如何在C#中使用`switch`来有条件地仅基于类型参数进行分支?

问题描述 投票:1回答:2

我的上下文是我正在构建一个简单的工厂方法来创建给定基类型的派生类型的实例。工厂方法只接受一个类型参数,即没有任何参数。使用if-else if结构显然可以实现这一点:

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.");
}

现在众所周知如何在C#(从C#7.0开始)中使用模式匹配来根据变量的类型进行分支,但这对于类型参数不起作用:

switch (T) { ... } // error: "'T' is a type, which is not valid in the given context"

要么...

switch (typeof(T))
{
    case Car c: ... 
    // err: "An expression of type 'Type' cannot be handled by a pattern of type 'Car'"
}

所以我想知道是否可以使用switch来实现相同的结果?


研究:我很惊讶以前没有被问过,但我找不到它。我发现this post有一个名字和几个答案非常接近,但它正在处理(数值)值类型和方法,其参数类型为T - 泛型类型参数。同样,this post也使用了一个参数。

c# .net generics switch-statement type-parameter
2个回答
1
投票

我知道您的问题专门询问有关使用switch语句的问题,但另一种方法是创建一个键入该类型的工厂字典。

您应该注意,此时,您正在执行类似于依赖注入的操作。如果Create方法没有创建X类型的车辆所需的信息,那么您要求的是类型为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.");
        }
    }
}

0
投票

编辑:请注意,我没有说明这是好还是坏。这完全是为了表明可以做到这一点。

这可以在C#7.0或更高版本中使用带模式匹配的switch块和when关键字来完成:

public Vehicle Create<T>() where T : Vehicle
{
    switch (true)
    {
        case bool x when typeof(T) == typeof(Car): return new Car();
        case bool x when typeof(T) == typeof(Truck): return new Truck();
        default: throw new ArgumentException(
            $"The type {typeof(T).Name} is not known by this method.");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.