Invariant inheritance problem

I'm trying to implement a strategy pattern to allow me to allow me to apply some "benefit" to an "account". In the code below, I can't add my implementation of an interface to a dictionary expecting the interface. I think it's some kind of contravariance problem, but it feels like I should be able to do this:

EDIT:
Since the answer seems to be that it's just not possible, any suggestions on how to achieve what I'm going for here?

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");
    }
}

EDITED - The formatting engine had removed everything in <angled brackets> , which made it rather impossible to understand. Sorry if this was confusing!

FreeBenefitStrategy implements IBenefitStrategy<FreeBenefit> . It can apply only FreeBenefits , not any other kind of benefit. It is not an IBenefitStrategy<BenefitBase> , so you can't put it in a collection of those. Logically, IBenefiteStrategy could be contravariant in BenefitBase , but this doesn't help you here - an IBenefitStrategy<BenefitBase> claims to be able to apply all kinds of benefits, so an IBenefitStrategy<BenefitBase> is-an IBenefitStrategy<FreeBenefit> , but the converse is not true - an IBenefitStrategy<FreeBenefit> cannot apply any BenefitBase .

I don't think there's any way to have a heterogenous collection like you want without using type-casting. If you think about it, there's no method that you can invoke on both an IBenefitStrategy<FreeBenefit> and an IBenefitStrategy<ExpensiveBenefit> beyond those that they share from object, so it makes sense that a variable of type object is the only thing that can point to either. If you want to keep them in the same dictionary, you'll need to make it a Dictionary<Type, object> . You could change GetStrategy to be generic and apply appropriate type-casting, but do be careful when looking up your dictionary - think what will happen if the object passed in is of a sub-class of FreeBenefit .


请参阅:协变和逆变常见问题解答

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.

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

public interface IBenefitStrategy<in T>
链接地址: http://www.djcxy.com/p/6706.html

上一篇: 在部署后处理nHibernate / Fluent nHibernate中的架构更新

下一篇: 不变继承问题