在 MEF 的泛型方法中转换 类

Casting classes in generic methods in MEF

我有一些 classes 和接口:

interface IAnimal { }
interface ILiveInZoo { }
class Cat : IAnimal, ILiveInZoo { }

此外,我还有一些方法和泛型方法:

class Context
{
    private static CompositionContainer Container = null;

    public ILiveInZoo GetWhoLivesInZoo(string name)
    {
        if (name == "Cat")
            return new Cat();
        return 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。在我将 cat 转换为 IAnimal 的用例中,在 GiveFood 方法中 typeof(T) 将是 IAnimal 而不是 Cat。第一个问题是: cat 变量的实例是 Cat class。为什么我投的时候typeof(T)会变成IAnimal? 我的问题是当我将 cat 转换为 IAnimal 接口时,在 GiveFood 方法中,GetExports 方法 returns 方法与 IAnimal 相关而不是 Cat class。我找到了解决该问题的解决方案,它正在使用反射:

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 class 并且在 GiveFood 中我可以获得与 Cat class 相关的方法。有没有其他方法(不使用反射)来解决这个问题?

如果您想在实现 IAnimal 的任何对象上调用该方法,但希望将对象的实际类型用作方法 GiveFood 的通用参数,反射方法可能是最少打扰。

一种 type-safe 但您可能想要查看的更详细的方法是 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);
    }
}


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

所以无论您的动物园管理员为了喂养动物需要知道什么,无论是它们的体重,它们是素食者、食肉动物还是杂食动物,它们每天需要喂食多少次,或者其他什么你需要了解他们。这些信息都需要在您的 IAnimal 界面上,并且每只动物都需要提供该信息的实现。

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

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

但是请注意,dynamic 在内部使用了反射(使用缓存以提高性能)。所以如果你真的想避免反射,其他答案中描述的访问者模式可能是要走的路。