Пользовательский контейнер IOC - нужна помощь для 2/3 типов - PullRequest
5 голосов
/ 07 декабря 2011

Справочная информация

Чтобы помочь мне лучше понять IOC и то, как его использовать, я хочу создать пример всех трех методов IOC: инъекция в конструктор, инъекция в сеттер и инъекция в интерфейс без необходимости использования третьейпартийные рамки.Я думаю, что у меня есть базовый пример внедрения конструктора, но я борюсь с сеттером и внедрением интерфейса.

Мой вопрос

Как бы вы подошли к решению написания интерфейса и внедрения сеттера с нуля?

Вот мои мысли, дайте мне знать, если я на правильном пути.

Внедрение интерфейса:

  1. Цикл по разрешенным объектам, созданным с помощью внедрения в конструктор, проверьте, какие интерфейсы реализованы в interfaceDependencyMap
  2. Определите некоторый интерфейс interfaceDependencyMap для привязкиинтерфейс к реализации.
  3. Разрешите реализацию, используя interfaceDependencyMap
  4. Назначьте соответствующее свойство объекту, инициализированному с помощью инжектора конструктора

Внедрение сеттера:

  1. Цикл по разрешенным объектам, созданным с использованием инжектора конструктора
  2. Определение некоторого вида setterInjectionMap
  3. Разрешение ожидаемого параметра из MethodInfo с использованием отображений конструктора
  4. Вызов передачи метода setterв разрешенном объекте параметра

Вот что у меня есть для инжектора конструктора

public class Program
{
    static void Main(string[] args)
    {
        //
        //instead of doing this:
        //
        //ICreditCard creditCard = new Visa();
        //var customer = new Customer(creditCard);
        //customer.Charge();


        var resolver = new Resolver();

        //map the types in the container
        resolver.Register<Customer, Customer>();
        resolver.Register<ICreditCard, Visa>();

        //because the customer constructor has an ICreditCard parameter
        //our container will automatically instantiate it recursively
        var customer = resolver.Resolve<Customer>();

        customer.Charge();

    }
}

public interface ICreditCard
{
    string Charge();
}

public class Visa : ICreditCard
{
    public string Charge()
    {
        return "Charging Visa";
    }
}

public class MasterCard : ICreditCard
{
    public string Charge()
    {
        return "Charging MasterCard";
    }
}

public class Customer
{
    private readonly ICreditCard _creditCard;

    public Customer(ICreditCard creditCard)
    {
        this._creditCard = creditCard;
    }

    public void Charge()
    {
        _creditCard.Charge();
    }
}


public class Resolver
{
    private Dictionary<Type, Type> dependencyMap = new Dictionary<Type, Type>();

    public T Resolve<T>()
    {
        return (T) Resolve(typeof (T));
    }

    private object Resolve(Type typeToResolve)
    {
        Type resolvedType = null;

        try
        {
            resolvedType = dependencyMap[typeToResolve];
        }
        catch
        {
            throw new Exception(string.Format("could not resolve type {0}", typeToResolve.FullName));
        }

        var firstConstructor = resolvedType.GetConstructors().First();
        var constructorParameters = firstConstructor.GetParameters();
        if (constructorParameters.Count() == 0)
            return Activator.CreateInstance(resolvedType);

        IList<object> parameters = constructorParameters.Select(parameterToResolve => Resolve(parameterToResolve.ParameterType)).ToList();

        return firstConstructor.Invoke(parameters.ToArray());
    }

    public void Register<TFrom, TTo>()
    {
        dependencyMap.Add(typeof (TFrom), typeof (TTo));
    }
}

1 Ответ

3 голосов
/ 08 декабря 2011

Это то, что вы ищете?

class Container
{
    class Registration
    {
        public Type RegistrationType;
        public Func<Container, object> Resolver;
    }

    List<Registration> registrations = new List<Registration>();

    public object Resolve(Type type)
    {
        return registrations
            .First(r => type.IsAssignableFrom(r.RegistrationType))
            .Resolver(this);
    }

    public T Resolve<T>()
    {
        return (T)Resolve(typeof(T));
    }

    public void Register<T>(Func<Container, T> registration) where T : class
    {
        registrations.Add(new Registration()
        {
            RegistrationType = typeof(T),
            Resolver = registration
        });
    }
}

Использование:

interface IDependency
{
    string GetName();
}

class ConcreteDependency : IDependency
{
    public string GetName()
    {
        return "Concrete Dependency";
    }
}

class ConstructorExample
{
    readonly IDependency dependency;

    public ConstructorExample(IDependency dependency)
    {
        this.dependency = dependency;
    }

    public string GetString()
    {
        return "Consumer of " + dependency.GetName();
    }
}

class SetterExample
{
    public IDependency Dependency { get; set; }

    public string GetString()
    {
        return "Consumer of " + Dependency.GetName();
    }
}

[TestMethod]
public void MyTestMethod()
{
    var container = new Container();
    container.Register<IDependency>(c => new ConcreteDependency());
    container.Register(c => new ConstructorExample(c.Resolve<IDependency>()));
    container.Register(c => new SetterExample() { Dependency = c.Resolve<IDependency>() });

    var constructor = container.Resolve<ConstructorExample>();
    Assert.AreEqual("Consumer of Concrete Dependency", constructor.GetString());

    var setter = container.Resolve<SetterExample>();
    Assert.AreEqual("Consumer of Concrete Dependency", setter.GetString());
}

Если вы хотите стать более продвинутым, я бы порекомендовал получить источник любого из них: SimpleInjector , Autofac , Ninject , StructureMap .

...