c# - 给定类型 T,解析并使用泛型类型 Calc 的其他对象处理 T
问题描述
对不起,如果标题不能很好地解释这个问题,但我想不出更好的名字。这可能最终会成为一个很长的问题,但请耐心等待。
假设我有两种类型的车辆Car
,Yacht
它们扩展了一个名为IVehicle
.
接口本身对于这个问题并不重要,但类包含描述它们的属性。
然后我有一个ITaxCalculator<T> where T : IVehicle
包含方法double Calculate(T vehicle);
。
然后有几个类实现了不同版本的ITaxCalculator<T>
(并采用不同的构造函数参数),例如:
LegitYachtTaxCalculator : ITaxCalculator<Yacht>
TaxHavenYachtTaxCalculator : ITaxCalculator<Yacht>
CarTaxCalculator : ITaxCalculator<Car>
然后我有一个List<IVehicle>
包含我的多辆汽车和游艇,我想计算我将不得不为它们支付的税款总额,同时能够切换用于计算每种类型税款的方法。
这是一些代码:
接口:
ITaxCalculator<T>
public interface ITaxCalculator<T> where T : IVehicle
{
double Calculate(T vehicle);
}
IVehicle
public interface IVehicle
{
string RegistrationPlate { get; }
}
实现
Car
public class Car : IVehicle
{
public Car(string registrationPlate)
{
RegistrationPlate = registrationPlate;
}
public string RegistrationPlate { get; }
}
Yacht
public class Yacht : IVehicle
{
public int WeightInTons { get; }
public Yacht(string registrationPlate, int weightInTons)
{
RegistrationPlate = registrationPlate;
WeightInTons = weightInTons;
}
public string RegistrationPlate { get; }
}
CarTaxCalculator : ITaxCalculator<Car>
public class CarTaxCalculator : ITaxCalculator<Car>
{
public double Calculate(Car vehicle)
{
Console.WriteLine($"Calculating tax for {vehicle.GetType().FullName} with plate {vehicle.RegistrationPlate} using {this.GetType().FullName}");
return 4999.95;
}
}
LegitYachtTaxCalculator : ITaxCalculator<Yacht>
public class LegitYachtTaxCalculator : ITaxCalculator<Yacht>
{
public double WeightMultiplier { get; }
public LegitYachtTaxCalculator(double weightMultiplier)
{
WeightMultiplier = weightMultiplier;
}
public double Calculate(Yacht vehicle)
{
Console.WriteLine($"Calculating tax for {vehicle.GetType().FullName} with plate {vehicle.RegistrationPlate} using {this.GetType().FullName}");
return vehicle.WeightInTons * WeightMultiplier;
}
}
TaxHavenYachtTaxCalculator : ITaxCalculator<Yacht>
public class TaxHavenYachtTaxCalculator : ITaxCalculator<Yacht>
{
public double Calculate(Yacht vehicle)
{
Console.WriteLine($"Calculating tax for {vehicle.GetType().FullName} with plate {vehicle.RegistrationPlate} using {this.GetType().FullName}");
return 0.0; // No taxes, woho!
}
}
主要的
static void Main(string[] args)
{
List<IVehicle> vehicles = new List<IVehicle>
{
new Car("PLT111"),
new Yacht("PLT333", 2500)
};
double totalTaxes = 0;
foreach (var vehicle in vehicles)
{
//Here I want to use engines which are configurable earlier to calculate the taxes for the vehicles
}
}
我试图以多种方式解决这个问题,例如
- IoC 容器
- 一个更轻量级的“解析器”变体,您可以在包含 ItaxCalculators 的类中注册引擎
Dictionary<Type, object>
并object
通过以下方法注册和解析public ITaxCalculator<T> GetTaxCalculator<T>() where T : IVehicle
public void RegisterTaxCalculator<T>(ITaxCalculator<T> calculator) where T : IPosition
- 在创建过程中将 ITaxCalculator 传递给 T 的每个实例,并让 T 通过使用它来计算自己的税款。
他们最终都会感到过于复杂,使用了不好的做法或需要太多的样板。所以我想知道构建这个的最好方法是什么?我从一开始就走错了轨道,还是有什么我还没有想到的模式?
谢谢!
解决方案
基本上,您需要一种方法来定义税收计算器和车辆之间的映射。
首先,使税收计算器非通用:
public interface ITaxCalculator
{
double Calculate(IVehicle vehicle);
}
发现所有非泛型实现、将它们加载到集合中并调用Calculate
.
为了处理特定的IVehicle
实现细节,声明税收计算器的基类,并使其通用:
public abstract class TaxCalculator<T> : ITaxCalculator
where T : IVehicle
{
public double Calculate(IVehicle vehicle)
{
// this is a single place, where cast is required
return Calculate((T)vehicle);
}
protected abstract double Calculate(T vehicle);
}
映射任务将我们引向元数据。
大多数 DI 容器都具有此功能。例如,这里是Autofac文档。
定义元数据类:
// This is metadata class;
// It defines vehicle type to calculate the tax value
public class TaxCalculatorMetadata
{
public Type VehicleType { get; set; }
}
注册ITaxCalculator
实现:
// Every implementation of ITaxCalculator must be registered like this
builder.RegisterType<CarTaxCalculator>()
.As<ITaxCalculator>()
.WithMetadata<TaxCalculatorMetadata>(m => m.For(_ => _.VehicleType, typeof(Car)));
现在您可以加载所有ITaxCalculator
实现,以某种方式过滤它们(“将它们插入“插槽””),并使用“插槽”中的车辆类型获取特定的计算器:
var vehicles = new List<Vehicle>
{
// ...
};
// assuming, that tax calculators were imported
// using IEnumerable<Lazy<ITaxCalculator, TaxCalculatorMetadata>>
foreach (var vehicle in vehicles)
{
var taxCalculator = taxCalculators
.First(_ => _.Metadata.VehicleType == vehicle.GetType());
Console.WriteLine(taxCalculator.Value.Calculate(vehicle));
}
推荐阅读
- html - html“服务器错误”中的Wordpress Elementor脚本
- python-3.x - 获取 FastAPI 响应的内容长度
- ruby-on-rails - 如何在 ActiveAdmin 中制作可排序的列?
- python - 如何合并不同数据框的两列,如果找到匹配项,使用 pandas 在新列中写入“True”
- python - 如何使打开的stl文件不漏水
- javascript - 警告:无法对未安装的组件执行 React 状态更新
- google-secret-manager - 如何从 Google Secret Manager 解码 base64 有效负载?
- ruby-on-rails - 如何使用 Rails 5.2 中的关系属性通过关系过滤 has_many 中的记录?
- python - Beautiful Soup 内联解析和
进字典
- python - 使用 Python 消除数据文件中的特定数字