Приведение классов в обобщенных методах в MEF - PullRequest
5 голосов
/ 30 мая 2019

У меня есть некоторые классы и интерфейс:

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.Почему, когда я разыграю его, typeof(T) будет IAnimal?Моя проблема заключается в том, что при преобразовании интерфейса cat в 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 классом.Есть ли другой способ (без использования отражения) решить эту проблему?

Ответы [ 3 ]

0 голосов
/ 03 июня 2019

В общем случае вы решаете эту проблему путем включения достаточного количества операций в интерфейсе, который вы выставляете для реализации этого метода.

Итак, все, что нужно знать вашему зоопарку для подачиживотное, будь то их вес, будь то вегетарианец, плотоядное животное, всеядное животное, сколько раз в день их нужно кормить, или все, что вам нужно знать о них.Вся эта информация должна быть в вашем IAnimal интерфейсе, и каждое животное должно обеспечивать реализацию этой информации.

0 голосов
/ 07 июня 2019

Простым и легким в использовании решением может быть использование dynamic :

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

Обратите внимание, однако, что dynamic использует Reflection внутренне (с кэшированием, чтобы сделать его немного болеепроизводительный).Так что, если вы действительно хотите избежать Reflection, шаблон посетителя, описанный в другом ответе, вероятно, поможет вам.

0 голосов
/ 03 июня 2019

Если вы хотите вызывать метод для всего, что реализует IAnimal, но хотите, чтобы фактический тип объекта использовался в качестве универсального параметра для метода GiveFood, подход к отражению, вероятно, наименее навязчив.

Типобезопасный, но более сложный подход, на который вы, возможно, захотите взглянуть, это шаблон посетителя (он же двойная рассылка, см. Код ниже). Это будет особенно уместно, если вы хотите, чтобы ваш метод 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);
    }
}


...