Можно ли использовать инициализатор объекта С# с помощью метода factory?
У меня есть класс со статическим factory методом. Я хочу вызвать factory для получения экземпляра класса, а затем выполнить дополнительную инициализацию, предпочтительно используя синтаксис инициализатора объекта С#:
MyClass instance = MyClass.FactoryCreate()
{
someProperty = someValue;
}
vs
MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
Ответы
Ответ 1
Нет. В качестве альтернативы вы можете принять лямбда в качестве аргумента, что также дает вам полный контроль над тем, какая часть процесса создания будет вызвана. Таким образом вы можете называть его следующим:
MyClass instance = MyClass.FactoryCreate(c=>
{
c.SomeProperty = something;
c.AnotherProperty = somethingElse;
});
Создание будет похоже на:
public static MyClass FactoryCreate(Action<MyClass> initalizer)
{
MyClass myClass = new MyClass();
//do stuff
initializer( myClass );
//do more stuff
return myClass;
}
Другой вариант - вернуть построитель (с неявным оператором литья в MyClass). Который вы бы назвали следующим:
MyClass instance = MyClass.FactoryCreate()
.WithSomeProperty(something)
.WithAnotherProperty(somethingElse);
Отметьте этот для строителя
Обе эти версии проверяются во время компиляции и имеют полную поддержку intellisense.
Третий вариант, требующий конструктора по умолчанию:
//used like:
var data = MyClass.FactoryCreate(() => new Data
{
Desc = "something",
Id = 1
});
//Implemented as:
public static MyClass FactoryCreate(Expression<Func<MyClass>> initializer)
{
var myclass = new MyClass();
ApplyInitializer(myclass, (MemberInitExpression)initializer.Body);
return myclass ;
}
//using this:
static void ApplyInitializer(object instance, MemberInitExpression initalizer)
{
foreach (var bind in initalizer.Bindings.Cast<MemberAssignment>())
{
var prop = (PropertyInfo)bind.Member;
var value = ((ConstantExpression)bind.Expression).Value;
prop.SetValue(instance, value, null);
}
}
Его середина между проверяется во время компиляции и не проверяется. Это требует некоторой работы, так как это принудительное выражение для заданий. Я думаю, что все остальное - это вариации подходов, уже упомянутых в ответах. Помните, что вы также можете использовать обычные назначения, подумайте, действительно ли вам нужно это.
Ответ 2
Вы можете использовать метод расширения, например следующий:
namespace Utility.Extensions
{
public static class Generic
{
/// <summary>
/// Initialize instance.
/// </summary>
public static T Initialize<T>(this T instance, Action<T> initializer)
{
initializer(instance);
return instance;
}
}
}
Вы бы назвали это следующим образом:
using Utility.Extensions;
// ...
var result = MyClass.FactoryCreate()
.Initialize(x =>
{
x.someProperty = someValue;
x.someProperty2 = someValue2;
});
Ответ 3
+1 на "Нет".
Здесь альтернатива пути анонимного объекта:
var instance = MyClass.FactoryCreate(
SomeProperty => "Some value",
OtherProperty => "Other value");
В этом случае FactoryCreate()
будет выглядеть примерно так:
public static MyClass FactoryCreate(params Func<object, object>[] initializers)
{
var result = new MyClass();
foreach (var init in initializers)
{
var name = init.Method.GetParameters()[0].Name;
var value = init(null);
typeof(MyClass)
.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
.SetValue(result, value, null);
}
return result;
}
Ответ 4
Нет, инициализатор объекта может использоваться только при вызове "new" с конструктором. Один из вариантов может заключаться в добавлении некоторых дополнительных аргументов в ваш метод factory, чтобы установить эти значения при создании объекта внутри factory.
MyClass instance = MyClass.FactoryCreate(int someValue, string otherValue);
Ответ 5
Как все сказали, нет.
Лямбда в качестве аргумента уже была предложена.
Более элегантный подход состоял бы в том, чтобы принять анонимность и установить свойства в соответствии с объектом. то есть.
MyClass instance = MyClass.FactoryCreate(new {
SomeProperty = someValue,
OtherProperty = otherValue
});
Это будет намного медленнее, поскольку, поскольку объект должен быть отражен для всех свойств.
Ответ 6
Вы можете использовать инициализатор объекта для уже созданного экземпляра с помощью следующего трюка. Вы должны создать простую обёртку объекта:
public struct ObjectIniter<TObject>
{
public ObjectIniter(TObject obj)
{
Obj = obj;
}
public TObject Obj { get; }
}
И теперь вы можете использовать его для инициализации своих объектов:
new ObjectIniter<MyClass>(existingInstance)
{
Obj =
{
//Object initializer of MyClass:
Property1 = value1,
Property2 = value2,
//...
}
};
Ответ 7
Нет, это то, что вы можете сделать только "inline". Вся функция factory может сделать для вас, чтобы вернуть ссылку.