【问题标题】:Declare generic type of instance声明实例的泛型类型
【发布时间】:2012-11-05 19:48:37
【问题描述】:

如何声明用于泛型实例的变量?

在控制器中,我需要创建依赖于支付类型的实例,并且每个类都有不同类型的参数。这就是我使用泛型类型的原因。 但是我不知道我需要设置什么类型来为每个支付类定义变量。

参数模型

public class PaymentModel
{   
    public string orderNo { get; set;}
}

public class CCPaymentModel : PaymentModel
{
    public string CCNo {get; set;}
    public string expDate {get; set;}
}

public class PaypalPaymentModel : PaymentModel
{
    public string paypalID {get; set;}
}

public class GooglePaymentModel : PaymentModel
{
    public string googleID {get; set;}
} 

接口类,我使用Generic类型参数,因为每种支付类型需要不同类型的参数。

public interface IPayment<T> where T : PaymentModel
{
    void makePayment(string orderNo);
    void makeRefund(T refundInfo);
}

模型,

public class SagePayment
    : IPayment<CreditCardPaymentInfo>
{
    public void MakePayment( CreditCardPaymentInfo creditCardPaymentInfo ) {
        // make payment
    }

    public void MakeRefund( CreditCardPaymentInfo creditCardPaymentInfo ) {
        // make refund
    }
}

public class GooglePayment
    : IPayment<GooglePaymentModel>
{
    public void MakePayment( GooglePaymentModel paymentInfo ) {
        // make payment
    }

    public void MakeRefund( GooglePaymentModel paymentInfo ) {
        // make refund
    }
}

public class PaypalPayment
    : IPayment<PayPalPaymentModel>
{
    public void MakePayment( PayPalPaymentModel paymentInfo ) {
        // make payment
    }

    public void MakeRefund( PayPalPaymentModel paymentInfo ) {
        // make refund
    }
}

控制器(创建实例)

public void Charge(string paytype,orderNo){

    IPayment<???> paymentProcess; // //Error    1   Using the generic type 'com.WebUI.Models.IPayment<T>' requires 1 type arguments
    Object payinfo;  //

    if (Regex.IsMatch(paytype, "^Credit Card"))
    {
        paymentProcess = new SagePayment();
        payinfo = getPaymentInfo(paytype, orderNo); // it return CCPaymentModel type object
    }
    else if (Regex.IsMatch(paytype, "^PayPal"))
    {
        paymentProcess = new PayPalPayment();
        payinfo = getPaymentInfo(paytype, orderNo); // it return PaypalPaymentModel type object
    }
    else if (Regex.IsMatch(paytype, "^Google"))
    {
        paymentProcess = new GooglePayment();
        payinfo = getPaymentInfo(paytype, orderNo); // it return GooglePaymentModel type object
    }

    paymentProcess.MakePayment(payinfo);
}

为了避免错误,我可以这样做,

public void Charge(string paytype,orderNo){

    if (Regex.IsMatch(paytype, "^Credit Card"))
    {
        IPayment<CCPaymentModel> paymentProcess = new SagePayment();
        payinfo = getPaymentInfo(paytype, orderNo);
        paymentProcess.MakePayment(payinfo);
    }
    else if (Regex.IsMatch(paytype, "^PayPal"))
    {
        IPayment<PaypalPaymentModel> paymentProcess = new PayPalPayment();
        payinfo = getPaymentInfo(paytype, orderNo);
        paymentProcess.MakePayment(payinfo);
    }
    else if (Regex.IsMatch(paytype, "^Google"))
    {
        IPayment<GooglePaymentModel> paymentProcess = new GooglePayment();
        payinfo = getPaymentInfo(paytype, orderNo);
        paymentProcess.MakePayment(payinfo);
    }
}

public void Refund(string paytype,orderNo){

    IPayment<???> paymentProcess; // //Error    1   Using the generic type 'com.WebUI.Models.IPayment<T>' requires 1 type arguments
    Object payinfo;  //

    if (Regex.IsMatch(paytype, "^Credit Card"))
    {
        paymentProcess = new SagePayment();
        payinfo = getPaymentInfo(paytype, orderNo); // it return CCPaymentModel type object
    }
    else if (Regex.IsMatch(paytype, "^PayPal"))
    {
        paymentProcess = new PayPalPayment();
        payinfo = getPaymentInfo(paytype, orderNo); // it return PaypalPaymentModel type object
    }
    else if (Regex.IsMatch(paytype, "^Google"))
    {
        paymentProcess = new GooglePayment();
        payinfo = getPaymentInfo(paytype, orderNo); // it return GooglePaymentModel type object
    }

    paymentProcess.MakeRefund(payinfo);
}

但我知道这是不对的。

谁知道,请指教。

【问题讨论】:

  • @millimoose 如何重组该设计,您能否解释得更详细一点?我现在正在学习 OOP,所以我想知道可以在实际项目中使用的正确方法。
  • 一般来说,当你在类集之间有这种 1:1 的对应关系时,这是一种设计味道,并且它暴露给这些类的客户。这是应该从控制器类中隐藏的东西。

标签: c# oop generics interface


【解决方案1】:

鉴于您的MakePayment 方法确实需要T,在单独的接口中声明它可能是最简单的。您可以让通用接口扩展非通用接口:

// Capitalization fixed to comply with conventions
public interface IPayment
{
    void MakePayment(string orderNo);
}

public interface IRefundPayment<T> : IPayment where T : PaymentModel
{
    void MakeRefund(T refundInfo);
}

或者只是将它们作为单独的接口:

public interface IPaymentHandler
{
    void MakePayment(string orderNo);
}

public interface IRefundHandler<T> where T : PaymentModel
{
    void MakeRefund(T refundInfo);
}

无论哪种方式,您都只需要 Charge 方法中的非泛型接口。

【讨论】:

    【解决方案2】:

    您可以使Charge()getPaymentInfo()PaymentModel 的类型而言是通用的:

    void Charge<TPaymentModel>(...) where TPaymentModel : PaymentModel {
        IPayment<TPaymentModel> payment = GetPayment<TPaymentModel>();
        // ...
        payment.MakePayment(getPaymentInfo<TPaymentModel>(...));
    }
    
    IPayment<TPaymentModel> GetPayment<TPaymentModel>() where TPaymentModel : IPaymentModel
    {
        // Create payment of appropriate type based on typeof(TPaymentModel)
    }
    
    TPaymentModel GetPaymentInfo(...) where TPaymentModel : PaymentModel
    {
        // Create payment model of appropriate type based on typeof(TPaymentModel)
    }
    

    这还是有点难看,为了解决这个问题,你可以引入一些新的类来隐藏接口中类型之间的对应关系,并使设计更合理:

    /// Abstracts over different ways of making payments
    interface IPaymentMaker 
    {
        void MakePayment(string payType, long orderNo);
        // MakeRefund etc.
    }
    
    /// Refactor code common to all payment types here, and handle the association 
    /// between payment and payment model.
    class PaymentMakerBase<TPaymentModel> : IPaymentMaker 
        where TPaymentModel : IPaymentModel 
    {
        void MakePayment(string payType, long orderNo) 
        {
            NewPayment().MakePayment(NewPaymentModel(payType, orderNo));
        }
    
        abstract IPayment<TPaymentModel> NewPayment();
        abstract TPaymentModel NewPaymentModel(string payType, long orderNo);
    }
    
    /// Handle only the differences between payment types that can't be put inside their
    /// implementations
    class PaypalPaymentMaker : PaymentMakerBase<PaypalPayment> 
    {
        IPayment<PaypalPayment> NewPayment() { ... }
        PaypalPayment NewPaymentModel(...) { ... }
    }
    
    static class PaymentMakerFactory 
    {
        /// The only "not type safe" part, handles parsing the payType string and 
        /// resolving it to the correct `PaymentMaker`
        public IPaymentMaker GetPaymentMaker(string payType) 
        {
            if (Regex.IsMatch(payType, ...)) 
            {
                // return appropriate payment maker for the payType
            }
            else if (...) 
            {
                // ...
            }
        }
    }
    

    那么,你的控制器代码就变成了这样:

    PaymentMakerFactory.GetPaymentMaker(payType).MakePayment(payType, orderNo);
    

    显然,上述设计可以通过删除冗余来改进(payType 可能在我包含它的所有地方都不需要),使其更“客观”(而不是传递相同的参数列表),或更方便(PaymentMakerFactory 可以可能会变成一个外观,它会创建正确的付款人,然后立即调用MakePayment)。

    【讨论】:

    • 感谢您的回答!我现在正在阅读您的答案 10 次,:) 但仍然难以理解,我会再阅读 100 次。顺便说一句,这是为了什么? NewPayment().MakePayment(NewPaymentModel(payType, orderNo));
    • @Expert 我建议将代码放入 VS 并单击它。它在功能上是您的代码,只是分成多个类。在我使用... 的地方,该代码与您已经拥有的代码相同(并且未在您的问题中列出)。基本上,您在控制器中的代码现在大部分位于PaymentMakerBase。除了每种支付类型不同的部分外,这些部分都在PaymentMakerBase 的子类中。 NewPayment().MakePayment(...) 对应于您的 if (Regex.IsMatch(...)) 子句的内容。
    猜你喜欢
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    • 2015-12-14
    • 2019-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    相关资源
    最近更新 更多