Разрешить экземпляр с несколькими конструкторами, используя единство - PullRequest
19 голосов
/ 13 января 2012

Я хотел бы создать экземпляр класса с использованием единицы, в которой класс имеет два конструктора с одинаковым количеством параметров.

Вот пример:

_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));

А вот конструкторы:

    public GradeType(string gradeTypeStringFromXmlFile)
    {
        _gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
    }

    public GradeType(Enum.GradeType gradeType)
    {
        _gradeType = gradeType;
    }

Если я пытаюсь сделать это, я получаю исключение, говорящее Тип GradeType имеет несколько конструкторов длины 1. Невозможно устранить неоднозначность .

Я могу установить атрибут [InjectionConstructor] для одного конструктора, чтобы он работал с одним, но тогда я не могу создать экземпляр с единицей, используя другой конструктор.

Есть ли какой-нибудь способ иметь несколько конструкторов с одинаковым количеством параметров и при этом использовать единицу для создания экземпляров?

Ответы [ 3 ]

28 голосов
/ 13 января 2012

Да, можно указать Unity, какой конструктор следует использовать, но вы можете сделать это только тогда, когда зарегистрируете свой тип с помощью InjectionConstructor. Если вы хотите использовать оба конструктора, это даже сложно, потому что вы должны назвать свои регистрации и использовать это имя при разрешении.

Образец, собранный с Unity версии 2.1.505:

var continer = new UnityContainer();

continer.RegisterType<IGradeType, GradeType>("stringConstructor", 
    new InjectionConstructor(typeof(string)));

continer.RegisterType<IGradeType, GradeType>("enumConstructor",
    new InjectionConstructor(typeof(EnumGradeType)));

IGradeType stringGradeType = continer.Resolve<IGradeType>("stringContructor" , 
    new DependencyOverride(typeof(string), "some string"));

IGradeType enumGradeType = continer.Resolve<IGradeType>("enumConstructor", 
    new DependencyOverride(typeof(EnumGradeType), EnumGradeType.Value));
1 голос
/ 04 декабря 2014

Альтернативный вариант с использованием отражения и следования шаблону стратегии .

1) Создать базовый класс для аргументов конструкторов

public abstract class ConstructorArgs
{
}

2) Создайте последовательность различных классов конкретных аргументов:

public class StringArg : ConstructorArgs
{
    public string _gradeTypeStringFromXmlFile { get; set; }

    public StringArg (string gradeTypeStringFromXmlFile)
    {
        this._gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile ;
    }
}

public class EnumArg : ConstructorArgs
{
    public Enum.GradeType _gradeType { get; set; }

    public EnumArg (Enum.GradeType gradeType)
    {
        this._gradeType = gradeType ;
    }
}

3) Теперь в вашем классе GradeType создайте методы, необходимые для Reflection. ParseArguments сканирует аргументы на предмет свойств и для каждого обнаруженного им значения копирует свое значение в соответствующее свойство GradeType с помощью SetProperty. Поскольку для сопоставления используется имя свойства, важно сохранить одинаковое имя свойства как для GradeType, так и для конкретного ConstructorArgs:

        private void SetProperty(String propertyName, object value)
        {
            var property = this.GetType().GetProperty(propertyName);
            if (property != null)
                property.SetValue(this, value);
        }
        private void ParseArguments(ConstructorArgs args)
        {
            var properties = args.GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                this.SetProperty(propertyInfo.Name, 
                   args.GetType().GetProperty(propertyInfo.Name).GetValue(args));
            }
        }

4) В своем классе GradeType создайте соответствующие свойства (учтите, что вы должны использовать точно такие же имена и типы, которые вы использовали в конкретных ConstructorArgs, но вы можете использовать любые модификаторы доступа, которые вам нравятся)

    public string _gradeTypeStringFromXmlFile { get; set; }
    public Enum.GradeType _gradeType { get; set; }

5) Создайте конструктор для вашего класса GradeType с параметром типа ConstructorArgs:

    public GradeType(ConstructorArgs args)
    {
        this.ParseArguments(args);
    }

6) Теперь вы можете зарегистрировать GradeType в Unity, используя один конструктор, но вы можете передавать различные типы в качестве аргументов при его разрешении:

    _unityContainer.RegisterType<IGradeType, GradeType>(
       new InjectionConstructor( typeof(ConstructorArgs) ));

    var args1 = new StringArg(gradeTypeStringFromXmlFile); // string
    IGradeType gradeType1 = _unityContainer.Resolve<IGradeType>(
       new ResolverOverride[]{new ParameterOverride("args", args1)});

    var args2 = new EnumArg(gradeType); // enum
    IGradeType gradeType2 = _unityContainer.Resolve<IGradeType>(
       new ResolverOverride[]{new ParameterOverride("args", args2)});

Если вы планируете многократно разрешать ваш тип в итерации, такой подход может быть не идеальным, поскольку Reflection сопровождается снижением производительности.

0 голосов
/ 13 января 2012

Удалите один конструктор и приведите строку к перечислению или наоборот, а затем разрешите, используя контейнер.

...