繁体   English   中英

给定其通用类型参数的类型,动态选择组件的类型

[英]Dynamically selecting the type of a component given the type of its generic type parameter

我似乎经常遇到一种情况,其中我有一个抽象类型,根据它具有的具体实现,需要对其进行不同的处理。

例如, abstract class Payment可以被子类化为class CreditCardclass 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设置PaymentTakerPayment之间的映射。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM