在MEF中以通用方法转换类

时间:2019-05-30 07:44:59

标签: c# generics reflection mef

我有一些类和接口:

interface IAnimal
{

}

class Cat : IAnimal, ILiveInZoo
{

}
interface ILiveInZoo
{

}

另外,我有一些方法和通用方法:

class Context
{
    public ILiveInZoo GetWhoLivesInZoo(string name)
    {
        if (name == "Cat")
            return new Cat();
        return null;
    }
    static CompositionContainer Container = null;
    public void GiveFood<T>(T animal) where T : IAnimal
    {
        var methods = Container.GetExports<Action<T, EventArgs>, AttributeMetadata>();
        //execute methods
    }
}

这是一个用例:

Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
   context.GiveFood(animal);
}

正如您在GiveFood方法中所见,我正在使用MEF。在用例中,当我将猫投放到IAnimal时,在GiveFood方法中,typeof(T)将是IAnimal而不是Cat。第一个问题是: cat变量的实例是Cat类。为什么当我投放它时,typeof(T)会是IAnimal。 我的问题是当我将猫转换为IAnimal接口时,在GiveFood方法中,GetExports方法将与IAnimal相关的方法返回给Cat类。我找到了解决该问题的解决方案,它使用了反射:

Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
   MethodInfo method = typeof(Context).GetMethod(nameof(Context.GiveFood));
   MethodInfo generic = method.MakeGenericMethod(animal.GetType());
   generic.Invoke(context, new object[] { animal });
}

现在typeof(T)是Cat类,在GiveFood中,我可以获得与Cat类相关的方法。是否有另一种方法(不使用反射)来解决此问题?

4 个答案:

答案 0 :(得分:0)

如果您使用直接强制转换而不是同时使用is运算符和if语句怎么办

try 
{
  context.GiveFood((Cat)animal);
}
catch(InvalidCastException ex) { }

答案 1 :(得分:0)

如果要在实现IAnimal的任何对象上调用该方法,但希望将对象的实际类型用作方法GiveFood的泛型参数,则反射方法可能是侵入性最低的方法

visitor pattern是一种类型安全但更复杂的方法,您可能要看一下(又称为双重调度,请参见下面的代码)。如果您希望GiveFood方法对IAnimal的某些特定类型的实现具有不同的实现,这将特别合适。


这涉及到定义一个新接口,

public interface IAnimalFeeder // the visitor
{
    void GiveFood(IAnimal animal); // main entry point

    // The generic implementation:

    void GiveFood<TAnimal>(TAnimal animal) where TAnimal : IAnimal;

    // + some specific implementations (if needed)

    void GiveFood(Cat cat);
    void GiveFood(Dog dog);
    void GiveFood(Fish fish);
    void GiveFood(Mouse mouse);
}

public class AnimalFeeder : IAnimalFeeder
{
    public void GiveFood(IAnimal animal)
    {
        animal.Accept(this);
    }

    public void GiveFood<TAnimal>(TAnimal animal) where TAnimal : IAnimal
    {
        // here the type parameter TAnimal will be the actual implementation

        // generic implementation
    }

    public void GiveFood(Cat cat)
    {
        // specific implementation
    }

    // etc...
}

public interface IAnimal
{
    void Accept(IAnimalFeeder feeder);
}

public class Dog : IAnimal
{
    public void Accept(IAnimalFeeder feeder)   // same code for each implementation of IAnimal
    {
        feeder.GiveFood(this);
    }
}


答案 2 :(得分:0)

您一般解决此问题的方法是在要公开的界面中包含足够的操作 来实现该方法。

因此,您的饲养员需要知道什么以喂养动物,体重是多少,是否是素食者,食肉动物,杂食动物,每天需要喂养多少次,或者您需要其他什么条件?了解他们。这些信息都需要放在您的IAnimal界面上,并且每只动物都需要提供该信息的实现。

答案 3 :(得分:0)

一个简单易用的解决方案可以是使用dynamic

Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
    context.GiveFood((dynamic)animal);
}

但是请注意,dynamic在内部使用了Reflection(通过缓存使其性能更高)。因此,如果您真的想避免反射,那么另一个答案中描述的“访问者模式”可能就是解决方法。