如何在 C# 中使用“switch”仅基于类型参数进行有条件的分支



我的上下文是,我正在构建一个简单的工厂方法,用于创建给定基类型的派生类型的实例。 工厂方法只接受类型参数,即没有任何参数。 这显然可以通过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来达到相同的结果?


研究:我很惊讶以前没有问过这个问题,但我找不到它。 我发现这篇文章有一个名称和一些非常接近的答案,但它正在处理具有类型T参数的(数字(值类型和方法 - 泛型类型参数。 同样,这篇文章也使用了一个参数。

我知道你的问题特别问到使用 switch 语句,但另一种选择是创建一个以类型为键的工厂字典。

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

编辑:请注意,我不是在做任何关于这是好是坏的声明。 这完全是为了表明这是可能的。

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

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

最新更新