问题描述
我想应用switch
语句模式匹配来确定我是否正在处理特定的泛型类型。内部类型可以是从抽象类继承的几种类型之一。这是一个示例:
using System;
namespace ConsoleApp1
{
abstract class Vehicle
{
public abstract string Name { get; }
}
internal class Sedan : Vehicle
{
public override string Name => "Sedan";
}
abstract class AbstractLoad
{
public abstract string Name { get; }
}
internal class Oil : AbstractLoad
{
public override string Name => "Oil";
}
internal class Lumber : AbstractLoad
{
public override string Name => "Lumber";
}
class Semi<TLoad> : Vehicle where TLoad : AbstractLoad
{
public override string Name => "Semi";
public TLoad Load { get; set; }
}
class Program
{
static void Main(string[] args)
{
var vehicle = new Semi<Oil> {Load = new Oil()};
DescribeVehicle(vehicle);
}
static void DescribeVehicle(Vehicle vehicle)
{
switch (vehicle)
{
case Sedan sedan:
Console.WriteLine("This vehicle is a sedan");
break;
case Semi<AbstractLoad> semi:
Console.WriteLine($"This vehicle is a semi carrying {semi.Load.Name}");
break;
default:
Console.WriteLine("We don't kNow what kind of vehicle this is.");
break;
}
}
}
}
我要打印,“这辆车是半载油”。但是,它会打印:“我们不知道这是哪种车辆。”有什么办法可以使它匹配Semi
的情况?
解决方法
考虑一下vehicle
的类型信息包含哪些内容:vehicle
是的确是ConsoleApp1.Semi<ConsoleApp1.Oil>
而不是ConsoleApp1.Semi<ConsoleApp1.AbstractLoad>
,对吗?这就是为什么类型不匹配的原因。
您在这里有几种可能性:
- 您显然可以使用多个
case
分支,每个分支都具有确切的类型,但这是不好的样式,因为它违背了目的,使您编写了大量重复的代码,并且极有可能忘记添加新的分支创建新的AbstractLoad
子类时分支。仅当您确实没有其他选择时,才执行此操作,但是在需要此操作时,它应该使您非常头痛。如果可能的话,避免使用它,这是不好的风格。不,真的。
// === DISCLAIMER ===
// Just for illustrative purposes.
// Don't use this solution,it's bad style.
using System;
namespace ConsoleApp1
{
abstract class Vehicle
{
public abstract string Name { get; }
}
internal class Sedan : Vehicle
{
public override string Name => "Sedan";
}
abstract class AbstractLoad
{
public abstract string Name { get; }
}
internal class Oil : AbstractLoad
{
public override string Name => "Oil";
}
internal class Lumber : AbstractLoad
{
public override string Name => "Lumber";
}
class Semi<TLoad> : Vehicle where TLoad : AbstractLoad
{
public override string Name => "Semi";
public TLoad Load { get; set; }
}
class Program
{
static void Main(string[] args) {
var vehicle = new Semi<Oil> { Load = new Oil() };
DescribeVehicle(vehicle);
}
static void DescribeVehicle(Vehicle vehicle) {
switch (vehicle) {
case Sedan _:
Console.WriteLine("This vehicle is a sedan");
break;
case Semi<Oil> _:
Console.WriteLine("This vehicle is a semi carrying Oil.");
break;
case Semi<Lumber> _:
Console.WriteLine("This vehicle is a semi carrying Lumber.");
break;
// Add new cases here (but rather don't use this solution at all)
default:
Console.WriteLine("We don't know what kind of vehicle this is.");
break;
}
}
}
}
- 您可以创建第二个
DescribeVehicle(Vehicle vehicle)
方法,而不是通过开关使用一个DescribeVehicle<TLoad>(Semi<TLoad> semi) where TLoad : AbstractLoad
方法。这是中等程度的代码重复,危险程度较小,但仍然不是很漂亮。
using System;
namespace ConsoleApp1
{
abstract class Vehicle
{
public abstract string Name { get; }
}
internal class Sedan : Vehicle
{
public override string Name => "Sedan";
}
abstract class AbstractLoad
{
public abstract string Name { get; }
}
internal class Oil : AbstractLoad
{
public override string Name => "Oil";
}
internal class Lumber : AbstractLoad
{
public override string Name => "Lumber";
}
class Semi<TLoad> : Vehicle where TLoad : AbstractLoad
{
public override string Name => "Semi";
public TLoad Load { get; set; }
}
class Program
{
static void Main(string[] args) {
var vehicle = new Semi<Oil> { Load = new Oil() };
DescribeVehicle(vehicle);
Console.ReadKey();
}
static void DescribeVehicle(Vehicle vehicle) {
switch (vehicle) {
case Sedan _:
Console.WriteLine("This vehicle is a sedan");
break;
default:
Console.WriteLine("We don't know what kind of vehicle this is.");
break;
}
}
static void DescribeVehicle<TLoad>(Semi<TLoad> semi) where TLoad : AbstractLoad {
Console.WriteLine($"This vehicle is a semi carrying {semi.Load.Name}");
}
}
}
真正的问题是,使用带有static
语句的独立switch
函数的方法已经成为问题的一部分:
- 由于您已经在使用继承,因此请使用
ToString()
方法。在Sedan
或Semi<TLoad>
类中使用所需的输出来覆盖它,并且理想情况下,对Oil
/Lumber
等类执行相同的操作。或者,如果您还需要ToString()
,则可以在string Describe()
和Vehicle
中创建自己的抽象基础方法AbstractLoad
,并在Sedan
/ {{ 1}}等。
Oil
,
更新:
我喜欢LWChris的第二种解决方案
效果很好:
class Program
{
static void Main(string[] args)
{
var vehicle = new Semi<Oil> { Load = new Oil() };
DescribeVehicle<Oil>(vehicle);
}
static void DescribeVehicle<TLoad>(Semi<TLoad> vehicle) where TLoad : AbstractLoad
{
switch (vehicle)
{
case Sedan _:
Console.WriteLine("This vehicle is a sedan");
break;
case Semi<TLoad> semi:
Console.WriteLine($"This vehicle is a semi carrying {semi.Load.Name}");
break;
default:
Console.WriteLine("We don't know what kind of vehicle this is.");
break;
}
}
}
=== 8
我想,您必须使用AbstractLoad
作为内部类型,因为Semi并非与Semi直接相同:
class Program
{
static void Main(string[] args)
{
var vehicle = new Semi<AbstractLoad> { Load = new Oil() };
DescribeVehicle(vehicle);
}
static void DescribeVehicle(Vehicle vehicle)
{
switch (vehicle)
{
case Sedan _:
Console.WriteLine("This vehicle is a sedan");
break;
case Semi<AbstractLoad> semi:
Console.WriteLine($"This vehicle is a semi carrying {semi.Load.Name}");
break;
default:
Console.WriteLine("We don't know what kind of vehicle this is.");
break;
}
}
}
此版本有效。
,解决方案2。 (由LWChris建议)
两种方法的版本:
https://dotnetfiddle.net/Widget/Preview?url=/Widget/3yVevr
class Program
{
static void Main(string[] args)
{
var vehicle = new Semi<Oil> { Load = new Oil() };
DescribeVehicle(vehicle);
var sedan = new Sedan();
DescribeVehicle(sedan);
}
static void DescribeVehicle(Vehicle vehicle)
{
switch (vehicle)
{
case Sedan _:
Console.WriteLine("This vehicle is a sedan");
break;
case Semi<AbstractLoad> semi:
Console.WriteLine($"This vehicle is a semi carrying {semi.Load.Name}");
break;
default:
Console.WriteLine("We don't know what kind of vehicle this is.");
break;
}
}
static void DescribeVehicle<TLoad>(Semi<TLoad> vehicle) where TLoad : AbstractLoad
{
switch (vehicle)
{
case Semi<TLoad> semi:
Console.WriteLine($"This vehicle is a semi carrying {semi.Load.Name}");
break;
default:
Console.WriteLine("We don't know what kind of vehicle this is.");
break;
}
}
}