Разрешить экземпляр с несколькими конструкторами, использующими единство
Я хотел бы создать экземпляр класса с использованием единицы, где класс имеет два конструктора с одинаковым количеством параметров.
Вот экземпляр:
_unityContainer.Resolve<IGradeType>(new ParameterOverride("gradeTypeStringFromXmlFile", gradeTypeStringFromXmlFile));
И вот конструкторы:
public GradeType( string gradeTypeStringFromXmlFile)
{
_gradeTypeStringFromXmlFile = gradeTypeStringFromXmlFile;
}
public GradeType(Enum.GradeType gradeType)
{
_gradeType = gradeType;
}
Если я попытаюсь сделать это, я получаю исключение: Тип GradeType имеет несколько конструкторов длиной 1. Невозможно устранить помехи.
Я могу установить атрибут [InjectionConstructor] над одним конструктором, чтобы он работал с ним, но тогда я не могу создать экземпляр с единством, используя другой конструктор.
Можно ли использовать несколько конструкторов с равным числом параметров и использовать единицу для создания экземпляров?
Ответы
Ответ 1
Да, можно сказать 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));
Ответ 2
Альтернативный вариант с использованием Reflection и Шаблон стратегии.
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 поставляется со снижением производительности.
Ответ 3
Удалите один конструктор и введите строку в перечисление или наоборот, а затем разрешите использование контейнера.