不变的继承问题

时间:2011-05-07 01:57:05

标签: c# casting contravariance

我正在尝试实施一种策略模式,允许我允许我对“帐户”应用一些“好处”。在下面的代码中,我无法将我的接口实现添加到期望接口的字典中。我认为这是一种逆变问题,但感觉我应该能够做到这一点:

修改
既然答案似乎是不可能的,那么有关如何实现我的目标的任何建议?

void Main()
{
    var provider = new BenefitStrategyProvider();

    var freeBenefit = new FreeBenefit();

    var strategy = provider.GetStrategy(freeBenefit);

    strategy.ApplyBenefit(freeBenefit, new Account());

}

public class BenefitStrategyProvider
{
    private Dictionary<Type, IBenefitStrategy<BenefitBase>> _strategies = new Dictionary<Type, IBenefitStrategy<BenefitBase>>();

    public BenefitStrategyProvider()
    {
        /* Why can't I add this? */
        _strategies.Add(typeof(FreeBenefit), new FreeBenefitStrategy());
    }

    public IBenefitStrategy<BenefitBase> GetStrategy(BenefitBase benefit)
    {
        return _strategies[benefit.GetType()];
    }

}

public class Account {}

public abstract class BenefitBase
{
    public string BenefitName {get;set;}    
}

public class FreeBenefit : BenefitBase {}

public interface IBenefitStrategy<T> where T: BenefitBase
{
    void ApplyBenefit(T benefit, Account account);
}

public class FreeBenefitStrategy : IBenefitStrategy<FreeBenefit>
{
    public void ApplyBenefit(FreeBenefit benefit, Account account)
    {
        Console.WriteLine("Free Benefit applied");
    }
}

3 个答案:

答案 0 :(得分:1)

请参阅:Covariance and Contravariance FAQ

How can I create variant generic interfaces and delegates myself?

The out keyword marks a type parameter as covariant, and the in keyword marks it as contravariant. The two most important rules to remember:
    You can mark a generic type parameter as covariant if it is used only as a method return type and is not used as a type of formal method parameters.
    And vice versa, you can mark a type as contravariant if it is used only as a type of formal method parameters and not used as a method return type.

答案 1 :(得分:1)

您需要将T添加到您的界面:

public interface IBenefitStrategy<in T>

答案 2 :(得分:1)

已编辑 - 格式化引擎删除了<angled brackets>中的所有内容,这使得它无法理解。对不起,如果这令人困惑!

FreeBenefitStrategy实施IBenefitStrategy<FreeBenefit>。它只能应用FreeBenefits,而不是任何其他类型的好处。它不是IBenefitStrategy<BenefitBase>,所以你不能将它放在那些集合中。从逻辑上讲,IBenefiteStrategy中的BenefitBase可能是逆变的,但这对您没有帮助 - IBenefitStrategy<BenefitBase>声称可以应用所有种类的好处,所以IBenefitStrategy<BenefitBase>IBenefitStrategy<FreeBenefit>,但相反的情况并非如此 - IBenefitStrategy<FreeBenefit>无法应用任何BenefitBase

我认为没有任何方法可以在不使用类型转换的情况下拥有您想要的异构集合。如果你考虑一下,除了他们从对象共享的那些之外,没有任何方法可以在IBenefitStrategy<FreeBenefit>IBenefitStrategy<ExpensiveBenefit>上调用,所以有意义的是对象类型的变量是唯一的这可以指向任何一个。如果您想将它们保存在同一个字典中,则需要将其设为Dictionary<Type, object>。您可以将GetStrategy更改为通用并应用适当的类型转换,但在查找字典时要小心 - 想想如果传入的对象属于FreeBenefit的子类会发生什么。