Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中使用“switch”来仅基于类型参数进行有条件的分支?_C#_.net_Generics_Switch Statement_Type Parameter - Fatal编程技术网

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

如何在C#中使用“switch”来仅基于类型参数进行有条件的分支?,c#,.net,generics,switch-statement,type-parameter,C#,.net,Generics,Switch Statement,Type Parameter,我的背景是,我正在构建一个简单的工厂方法,用于创建给定基类型的派生类型的实例。factory方法只接受一个类型参数,即没有任何参数。这显然可能与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 (typeo

我的背景是,我正在构建一个简单的工厂方法,用于创建给定基类型的派生类型的实例。factory方法只接受一个类型参数,即没有任何参数。这显然可能与
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.");
}
或者

因此,我想知道是否可以使用
开关来实现相同的结果?



研究:我很惊讶以前没有人问过这个问题,但我找不到。我发现它有一个名字和一些答案,非常接近,但它处理的是(数值)值类型和方法,它们的参数类型为
T
——泛型类型参数。类似地,还使用了一个参数。

编辑:请注意,我并没有就这是好是坏发表任何声明。这完全是为了表明这是可能的

这可以在C#7.0或更高版本中使用带模式匹配的
开关
块和
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.");
    }
}
public Vehicle Create(),其中T:Vehicle
{
开关(真)
{
当typeof(T)=typeof(Car)时的case bool x:返回新车();
当typeof(T)=typeof(Truck)时的case bool x:返回新Truck();
默认值:抛出新ArgumentException(
$“此方法不知道类型{typeof(T).Name}”;
}
}

我知道您的问题是关于使用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.");
        }
    }
}
公共类车辆:车辆{}
公共类卡车:车辆{}
公共抽象类车辆
{
专用静态只读iRadonlyDictionary车辆工厂=新字典
{
{typeof(Car),()=>新车()},
{typeof(Truck),()=>新Truck()}
};
公共静态车辆创建(),其中T:Vehicle,new()
{
if(车辆工厂TryGetValue(类型(T),出厂值))
{
返回工厂();
}
其他的
{
抛出新的ArgumentException(
$“此方法不知道类型{typeof(T).Name}”;
}
}
}

否-恐怕您被if/else卡住了。至少要记住,JIT知道这种模式,并且可以对其进行优化。你的答案只是滥用开关来编写if/else——我不确定你是否真的可以声称这是if/else的可行替代方案!我可以使用异常过滤器编写if/else。这是不是意味着我应该?绝对不是。对于a,if/else是O(n)(忽略任何JIT优化),而dictionary是O(1)。对于大量的条目,字典可能会在if/else中脱口而出。使用
RuntimeTypeHandle
获得额外的速度点。有关更多荒谬的建议,请参阅
where T:new()/*…*/开关(新的T()){…}
。那太可怕了。你为什么要在你的代码中使用它?@Patrickhoffman——这只是一个POC,这完全是你的观点,除非你能用事实来证明。它有一些优雅之处。这是你越来越感兴趣的事情,那么问题是“在不使用显式if/else的情况下,有什么深奥的方法来编写if/else?”?我可以想出一些可怕的答案…问题不在于if/then vs.switch。运行时类型检查将产生不可预测的运行时错误。如果我写
Create()
,它会编译,因为
Bicycle:Vehicle
,但在运行时它会说,“哎呀,不是那种
Vehicle
”,这很糟糕。在大多数情况下(不是所有情况下),如果我们正在执行这种运行时类型检查,则会出现一些错误。
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.");
    }
}
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.");
        }
    }
}