use*_*548 10 c# generics interface factory-method
我已经实施了一项车辆服务,负责维修汽车和卡车等车辆:
public interface IVehicleService
{
void ServiceVehicle(Vehicle vehicle);
}
public class CarService : IVehicleService
{
void ServiceVehicle(Vehicle vehicle)
{
if (!(vehicle is Car))
throw new Exception("This service only services cars")
//logic to service the car goes here
}
}
Run Code Online (Sandbox Code Playgroud)
我还有一个车辆服务工厂,负责根据传入工厂方法的车辆类型创建车辆服务:
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
if (vehicle is Car)
{
return new CarService();
}
if (vehicle is Truck)
{
return new TruckService();
}
throw new NotSupportedException("Vehicle not supported");
}
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是这个CarService.ServiceVehicle
方法.它接受一个Vehicle
理想情况下它应该接受一个Car
,因为它知道它只会服务汽车.所以我决定更新此实现以使用泛型:
public interface IVehicleService<T> where T : Vehicle
{
void ServiceVehicle(T vehicle);
}
public class CarService : IVehicleService<Car>
{
void ServiceVehicle(Car vehicle)
{
//this is better as we no longer need to check if vehicle is a car
//logic to service the car goes here
}
}
public class VehicleServiceFactory
{
public IVehicleService<T> GetVehicleService<T>(T vehicle) where T : Vehicle
{
if (vehicle is Car)
{
return new CarService() as IVehicleService<T>;
}
if (vehicle is Truck)
{
return new TruckService() as IVehicleService<T>;
}
throw new NotSupportedException("Vehicle not supported");
}
}
Run Code Online (Sandbox Code Playgroud)
我目前遇到的问题是在调用此工厂时如下:
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle();
var vehicleService = factory.GetVehicleService(vehicle); // this returns null!
vehicleService.ServiceVehicle(vehicle);
Run Code Online (Sandbox Code Playgroud)
GetVehicleService
返回null
,我想是因为我将基类型传递Vehicle
给此方法,因此T
将进行求值,Vehicle
并且不可能从CarService
(实现IVehicleService<Car>
)转换为有效的返回类型IVehicleService<Vehicle>
(如果我是,请纠正我)错误).
希望得到一些关于如何解决这个问题的指导.
您面临的问题与 C# 推论的泛型类型有关。
Vehicle vehicle = GetVehicle();
Run Code Online (Sandbox Code Playgroud)
这行会给你带来麻烦,因为vehicle
你传入的变量类型
var vehicleService = factory.GetVehicleService(vehicle); // this returns null!
Run Code Online (Sandbox Code Playgroud)
是 类型Vehicle
而不是Car
( 或)类型Truck
。因此,您的工厂方法推导出的类型GetVehicleService<T>
(T) 是Vehicle
。但是,在 GetVehicleService 方法中,如果给定类型无法按您希望的方式进行强制转换,则执行安全强制转换 ( as
) 并返回。null
如果改成直接演员
return (IVehicleService<T>) new CarService();
Run Code Online (Sandbox Code Playgroud)
您将看到,调试器将在此行捕获 InvalidCastException。这是因为您CarService
实现了IVehicleService<Car>
,但程序实际上尝试将其转换为IVehicleService<Vehicle>
您未实现的对象CarService
,因此抛出异常。
如果你完全移除演员阵容
return new CarService();
Run Code Online (Sandbox Code Playgroud)
您甚至会在编译时收到错误,告诉您这些类型不能相互转换。
不幸的是,我不知道可以由 C# 处理的简洁解决方案。但是,您可以为您的服务创建一个抽象基类,实现一个非通用接口:
public interface IVehicleService
{
void ServiceVehicle(Vehicle vehicle);
}
public abstract class VehicleService<T> : IVehicleService where T : Vehicle
{
public void ServiceVehicle(Vehicle vehicle)
{
if (vehicle is T actual)
ServiceVehicle(actual);
else
throw new InvalidEnumArgumentException("Wrong type");
}
public abstract void ServiceVehicle(T vehicle);
}
public class CarService : VehicleService<Car>
{
public override void ServiceVehicle(Car vehicle)
{
Console.WriteLine("Service Car");
}
}
public class TruckService : VehicleService<Truck>
{
public override void ServiceVehicle(Truck vehicle)
{
Console.WriteLine("Service Truck");
}
}
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
if (vehicle is Car)
{
return new CarService();
}
if (vehicle is Truck)
{
return new TruckService();
}
throw new NotSupportedException("Vehicle not supported");
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,工厂现在是非通用的,接口也是如此(就像您之前的接口一样)。然而,服务的抽象基类现在可以处理类型并在类型不匹配时抛出异常(不幸的是仅在运行时)。
如果您的工厂有很多不同的类型并且您想要保存数十条if
语句,您可以使用属性做一些解决方法。
首先,创建一个ServiceAttribute类:
[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
public Type Service { get; }
public ServiceAttribute(Type service)
{
Service = service;
}
}
Run Code Online (Sandbox Code Playgroud)
然后将此属性附加到您的车辆类别:
[Service(typeof(TruckService))]
public class Truck : Vehicle
// ...
Run Code Online (Sandbox Code Playgroud)
并像这样改变你的工厂:
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
var attributes = vehicle.GetType().GetCustomAttributes(typeof(ServiceAttribute), false);
if (attributes.Length == 0)
throw new NotSupportedException("Vehicle not supported");
return (IVehicleService) Activator.CreateInstance(((ServiceAttribute)attributes[0]).Service);
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法不使用反射,因此与 if 语句相比应该不会那么慢。
归档时间: |
|
查看次数: |
1231 次 |
最近记录: |