[英]Dynamically selecting the type of a component given the type of its generic type parameter
我似乎经常遇到一种情况,其中我有一个抽象类型,根据它具有的具体实现,需要对其进行不同的处理。
例如, abstract class Payment
可以被子类化为class CreditCard
或class StoredCredit
。 为了实际处理付款,我们要使用
interface IPaymentTaker {
PaymentProcessingResult Process(Payment payment); }
即要么
class CreditCardPaymentTaker : IPaymentTaker { ... }
要么
class StoredCreditPaymentTaker : IPaymentTaker { ... }
过去,我已经将IDictionary注入到父组件中,然后完成了
_paymentTakers[payment.GetType()].Process(payment);
缺点是IPaymentTaker
实现的类型不够强,因此Process方法的第一位必须是:
Process(Payment payment)
{
var creditCardPayment = payment as CreditCardPayment;
if (creditCardPayment == null)
throw new Exception("Payment must be of type CreditCard");
}
我确定我要实现的模式必须有一个名称,但我不知道它是什么!
理想情况下,我会
(a)能够仅根据付款类型实例化PaymentProcessor,而无需创建字典;
(b)能够使用强类型的PaymentProcessor,它们仅接受他们可以使用的子类。
有谁有解决这个问题的灵巧方法?
interface IPayment
{
IPaymentTaker Taker {get;}
}
class CreditCardPayment : IPayment
{
IPaymentTaker Taker{ get {return new CreditCardPaymentTaker();}}
}
payment.Taker.Process(payment);
您可以通过访问者解决此问题:
interface IPaymentVisitor {
void Visit(CreditCard payment);
void Visit(StoredCredit payment);
}
abstract class Payment {
public abstract void Accept(IPaymentVisitor visitor);
}
class CreditCard : Payment {
public override void Accept(IPaymentVisitor visitor) {
visitor.Visit(this);
}
}
class StoredCredit : Payment {
public override void Accept(IPaymentVisitor visitor) {
visitor.Visit(this);
}
}
class PaymentTaker : IPaymentVisitor, IPaymentTaker {
public void Visit(CreditCard payment) {
// ...
}
public void Visit(StoredCredit payment) {
// ...
}
public PaymentProcessingResult Process(Payment payment) {
payment.Accept(this);
// ...
}
}
如果您仍然想分隔不同的付款人,或者您的层次结构不稳,可以使用非循环访问者(pdf) :
interface IPaymentVisitor {
}
interface IPaymentVisitor<TPayment> : IPaymentVisitor where TPayment : Payment {
void Visit(TPayment payment);
}
abstract class Payment {
public abstract void Accept(IPaymentVisitor visitor);
}
class CreditCard : Payment {
public override void Accept(IPaymentVisitor visitor) {
if (visitor is IPaymentVisitor<CreditCard>) {
((IPaymentVisitor<CreditCard>)visitor).Visit(this);
}
}
}
class StoredCredit : Payment {
public override void Accept(IPaymentVisitor visitor) {
if (visitor is IPaymentVisitor<StoredCredit>) {
((IPaymentVisitor<StoredCredit>)visitor).Visit(this);
}
}
}
class CreditCardPaymentTaker : IPaymentVisitor<CreditCard>, IPaymentTaker {
public void Visit(CreditCard payment) {
// ...
}
public PaymentProcessingResult Process(Payment payment) {
payment.Accept(this);
// ...
}
}
class StoredCreditPaymentTaker : IPaymentVisitor<StoredCredit>, IPaymentTaker {
public void Visit(StoredCredit payment) {
// ...
}
public PaymentProcessingResult Process(Payment payment) {
payment.Accept(this);
// ...
}
}
即使James的方法很理想,使用IoC容器也可能很困难。 这是我基于反射或动力学的方法。 执行以下操作将使您仍然可以使用IoC设置PaymentTaker
和Payment
之间的映射。
public class Payment
{
}
public class CreditCardPayment : Payment
{
}
public class StoreCreditPayment : Payment
{
}
public interface IPaymentTaker
{
}
public interface IPaymentTaker<T> : IPaymentTaker
{
void Process(T payment);
}
public static class PaymentTaker
{
public static void Process(Payment payment)
{
var paymentType = payment.GetType();
// You would have these already setup and loaded via your IOC container...
var paymentTakers = new Dictionary<Type, IPaymentTaker>();
paymentTakers.Add(typeof(CreditCardPayment), new CreditCardPaymentTaker());
paymentTakers.Add(typeof(StoreCreditPayment), new StoreCreditPaymentTaker());
// Get the payment taker for the specific payment type.
var paymentTaker = paymentTakers[paymentType];
// Execute the 'Process' method.
paymentTaker.GetType().GetMethod("Process").Invoke(paymentTaker, new object[]{ payment });
// If .NET 4.0 - dynamics can be used.
// dynamic paymentTaker = paymentTakers[paymentType];
// paymentTaker.Process((dynamic)payment);
}
}
public class CreditCardPaymentTaker : IPaymentTaker<CreditCardPayment>
{
public void Process(CreditCardPayment payment)
{
Console.WriteLine("Process Credit Card Payment...");
}
}
public class StoreCreditPaymentTaker : IPaymentTaker<StoreCreditPayment>
{
public void Process(StoreCreditPayment payment)
{
Console.WriteLine("Process Credit Card Payment...");
}
}
然后您可以像这样使用它:
var cc = new CreditCardPayment();
PaymentTaker.Process(cc);
如果您可以确保Payment和PaymentTaker的名称匹配,则可以使用以下方式:
Process(Payment payment)
{
String typeName = "YourPathToPaymentTakers." + payment.GetType().Name + "Taker";
Type type = typeof(IPaymentTaker).Assembly.GetType(typeName);
IPaymentTaker taker = (IPaymentTaker)Activator.CreateInstance(type);;
}
我过去曾使用过这种方法,但是如果您不能100%控制类的名称,则可能会遇到问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.