2011-05-07 16 views
7

Estoy tratando de implementar un patrón de estrategia para permitirme aplicar algún "beneficio" a una "cuenta". En el siguiente código, no puedo agregar mi implementación de una interfaz a un diccionario que espera la interfaz. Creo que es algún tipo de problema contravarianza, pero se siente como si tuviera que ser capaz de hacer esto:Problema de herencia invariable

EDIT:
Dado que la respuesta parece ser que simplemente no es posible, alguna sugerencia sobre cómo lograr lo Voy por aquí?

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

¿Está utilizando .NET 4? – Tejs

+0

@Tejs sí Estoy usando .net 4.0 – scottm

Respuesta

1

editado - El motor de formateo había eliminado todo en <angled brackets>, que hacía bastante imposible de entender Lo siento si esto fue confuso!

FreeBenefitStrategy implementa IBenefitStrategy<FreeBenefit>. Puede aplicar solo FreeBenefits, no cualquier otro tipo de beneficio. No es un IBenefitStrategy<BenefitBase>, por lo que no puede ponerlo en una colección de esos. Lógicamente, IBenefiteStrategy podrían contravariant en BenefitBase, pero esto no le ayuda aquí - un IBenefitStrategy<BenefitBase> dice ser capaz de aplicar todos los tipo de beneficios, por lo que un IBenefitStrategy<BenefitBase> es-un IBenefitStrategy<FreeBenefit>, pero lo contrario no es cierto - un IBenefitStrategy<FreeBenefit> no se puede aplicar ningún BenefitBase.

No creo que haya ninguna manera de tener una colección heterogénea como desees sin usar el método de conversión. Si lo piensas, no hay un método que puedas invocar en un IBenefitStrategy<FreeBenefit> y un IBenefitStrategy<ExpensiveBenefit> más allá de los que comparten desde el objeto, por lo que tiene sentido que una variable de tipo de objeto sea lo único que pueda señalar. Si desea mantenerlos en el mismo diccionario, deberá convertirlo en Dictionary<Type, object>. Puede cambiar GetStrategy para que sea genérico y aplicar el moldeado de texto apropiado, pero tenga cuidado al buscar su diccionario: piense qué sucederá si el objeto pasado es de una subclase de FreeBenefit.

1

Ver: 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

Es necesario agregar a cabo T a su interfaz:

public interface IBenefitStrategy<in T> 
+0

Ese final me da este error: Varianza inválida: El parámetro de tipo 'T' debe ser contravariante válido en 'IBenefitStrategy . ApplyBenefit (T, UserQuery.Account)'. 'T' es covariante. – scottm

+0

Eso es porque debería ser 'en T', no' out T'. 'T 'aquí es una variable de tipo, y su interfaz solo" capta "objetos de tipo' T' y nunca los "saca", entonces este es un ciudadano modelo para la contravarianza. –

+0

@Paul Z, pero cuando lo hago en T, ¡no puedo agregar la implementación! – scottm

Cuestiones relacionadas