Решите только один элемент в List‹Lazy‹T›› с помощью Ninject

Это мой сценарий:

 public class Foo
    {
        private readonly List<Lazy<IAnimal>> _animals;

        public Foo(List<Lazy<IAnimal>> animals )
        {
            _animals = animals;
        }

        public void Bark()
        {
            //Line: *
            var dog = _animals.First(p => p.GetType() == typeof (Dog)).Value;
        }

        Public void Mio()
        {
            //Line: *
            var dog = _animals.First(p => p.GetType() == typeof (Cat)).Value;
        }
    }

    public class Dog:IAnimal
    {
    }

    public class Cat:IAnimal
    {
    }

    public interface IAnimal
    {
    }

Вопросы:

Здесь список животных лениво вводится в класс Foo.

Я хочу сделать что-то вроде Line * с Ninject. Как вы знаете, проблема в том, что перед разрешением класса GetType() возвращает Null. Итак, как я могу разрешить только один из элементов в списке, когда захочу?

Может ли Ninject сделать такое вообще, или мне придется изменить свой DI-контейнер?


person Mehrdad Kamelzadeh    schedule 24.07.2013    source источник
comment
Поскольку вас интересует один экземпляр Dog в методе Bark, почему бы вам просто не внедрить этот единственный экземпляр Dog в конструктор Foo?   -  person Steven    schedule 25.07.2013
comment
@Стивен, посмотри мое обновление.   -  person Mehrdad Kamelzadeh    schedule 25.07.2013
comment
Так почему бы не ввести собаку и кошку? Если у вас еще больше таких методов, возможно, вы нарушаете принцип единой ответственности.   -  person Steven    schedule 25.07.2013
comment
@Стивен, ты знаешь, у меня на самом деле есть несколько кнопок на моем MainWindow, которые отвечают за звук животного (первая кнопка для собаки, вторая для кошки и так далее). Класс Foo на самом деле является моим MainWindowViewModel, который я привязал к кнопкам. Command к методам в нем. Сценарий на самом деле намного сложнее, чем этот пример кода. Как вы знаете, я использовал эти термины только для простоты. на самом деле у меня есть тяжелое дерево классов, и я думаю, что если я захочу разрешить их в корне моей композиции, это повлияет на производительность.   -  person Mehrdad Kamelzadeh    schedule 25.07.2013


Ответы (2)


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

Это хорошо подходит для Lazy‹T, TMetadata›, который является частью сборки System.ComponentModel.Composition:

public class Foo
{
    private readonly List<Lazy<IAnimal, Type>> _animals;

    public Foo(List<Lazy<IAnimal, Type>> animals)
    {
        _animals = animals;
    }

    public void Bark()
    {
        var dog = _animals.First(p => p.Metadata == typeof(Dog)).Value;
    }
}

Обновить

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

var list = new List<Lazy<IAnimal, Type>>();

list.Add(new Lazy<IAnimal, Type>(() => kernel.Get<Dog>(), typeof(Dog)));
list.Add(new Lazy<IAnimal, Type>(() => kernel.Get<Pussy>(), typeof(Pussy)));
list.Add(new Lazy<IAnimal, Type>(() => kernel.Get<Horse>(), typeof(Horse)));

kernel.Bind<List<Lazy<IAnimal, Type>>().ToConstant(list);
person Steven    schedule 24.07.2013
comment
Спасибо за ваш ответ @Steven. Но как я могу Bind, что теперь в корне моей композиции? как я могу отправить Metadata? - person Mehrdad Kamelzadeh; 25.07.2013
comment
Вам придется сделать это вручную. Для этого нет встроенной поддержки. Единственный контейнер со встроенной поддержкой Lazy‹T, TMetadata› — это Autofac, но я бы не стал переключаться только из-за этого. Зарегистрировать свою коллекцию должно быть довольно легко (хотя я не эксперт Ninject). - person Steven; 25.07.2013
comment
Извините, но я не знаю, как это сделать вручную? Если Ninject этого не поддерживает, то как я могу это сделать? - person Mehrdad Kamelzadeh; 25.07.2013
comment
Я сделал это, но столкнулся с ошибкой, что это слишком долго, чтобы написать это здесь, в комментарии. поэтому я удаляю некоторые ненужные строки, чтобы поместиться здесь. - person Mehrdad Kamelzadeh; 25.07.2013
comment
Ninject.ActivationException не было обработано пользовательским кодом. Сообщение = Ошибка при активации Lazy{IAnimal, Type} с использованием неявной самопривязки Lazy{IAnimal, Type} Несколько конструкторов имеют одинаковый приоритет. Укажите конструктор, используя синтаксис ToConstructor, или добавьте атрибут Inject. Путь активации: 2) Внедрение зависимости Lazy{IAnimal, Type} в животных параметров конструктора типа Foo 1) Запрос предложений Foo: 1) Убедитесь, что тип реализации имеет общедоступный конструктор. 2) Если вы реализовали шаблон Singleton, вместо этого используйте привязку с InSingletonScope(). - person Mehrdad Kamelzadeh; 25.07.2013
comment
@MehrdadKamelzadeh: замените ToInstance на ToConstant. - person Steven; 25.07.2013
comment
Да, я сделал это. ToInstance() метода нет. Я изменил это ToConstant(), но получил ошибку. Кстати, я хочу отправить список животных. который я выберу во время выполнения, чтобы быть собакой, кошкой или т. д. - person Mehrdad Kamelzadeh; 25.07.2013
comment
Как вы думаете, мне нужно изменить свой DI-контейнер? потому что я застрял на этом в течение 2 дней. Я действительно не знаю, как это решить. ПОМОЩЬ! ;) - person Mehrdad Kamelzadeh; 25.07.2013
comment
Навряд ли. Зачем вообще нужен Lazy<T>? Мне трудно рассуждать об этом, но, возможно, захочется пересмотреть ваш дизайн. Почему бы вам не создать новый вопрос на SO, чтобы обсудить эту часть вашего приложения. Если вам нужны ценные отзывы о дизайне, пожалуйста, используйте настоящие имена классов (и, возможно, немного справочной информации о том, что делает этот класс). Если дадите сюда ссылку, попробую посмотреть. - person Steven; 25.07.2013
comment
Вы знаете, что ваш ответ - это именно то, что я искал. вы полностью поняли мой вопрос. Но если я просто могу выяснить причину ошибки, я готов. в любом случае, я думаю, очень сложно объяснить весь мой код их настоящими именами. Можно ли отправить все мое решение куда-нибудь, чтобы вы его увидели? - person Mehrdad Kamelzadeh; 25.07.2013

Вы можете использовать метод расширения OfType, предоставленный в пространстве имен System.Linq.

var dog = _animals.OfType<Dog>().First();
person Hemario    schedule 24.07.2013
comment
Как я уже сказал, тип не определяется до разрешения. так это нельзя сделать. - person Mehrdad Kamelzadeh; 24.07.2013