конструктор как делегат-возможно ли это в C#?
у меня есть класс, как показано ниже:
class Foo
{
public Foo(int x) { ... }
}
и мне нужно передать определенному методу такой делегат:
delegate Foo FooGenerator(int x);
можно ли передать конструктор непосредственно как FooGenerator
значением, без типа:
delegate(int x) { return new Foo(x); }
?
EDIT: для моего личного использования вопрос относится к .NET 2.0, но подсказки/ответы для 3.0+ также приветствуются.
7 ответов
нет, среда CLR не позволяет связывать делегатов с ConstructorInfo
.
static T Make<T>(Action<T> init) where T : new()
{
var t = new T();
init(t);
return t;
}
использование
var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });
Я предполагаю, что вы обычно делаете что-то подобное в рамках реализации фабрики, где фактические типы не известны во время компиляции...
во-первых, обратите внимание, что более простой подход может быть шагом после создания init, тогда вы можете использовать дженерики:
static T Create<T>({args}) where T : class, ISomeInitInterface, new() {
T t = new T();
t.Init(args);
return t;
}
затем вы можете использовать MakeGenericMethod
и/или CreateDelegate
.
в противном случае; вы можете сделать это на лету с Expression
(3.5) или DynamicMethod
(2.0).
на Expression
подход легче код:
var param = Expression.Parameter(typeof(int), "val");
var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
var lambda = Expression.Lambda<Func<int, Foo>>(
Expression.New(ctor, param), param);
var func = lambda.Compile();
Foo foo = func(123);
string s = foo.ToString(); // proof
или (используя DynamicMethod
):
ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
DynamicMethod dm = new DynamicMethod("Create", typeof(Foo),
new Type[] { typeof(int) }, typeof(Foo), true);
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Ret);
Converter<int, Foo> func = (Converter<int, Foo>)
dm.CreateDelegate(typeof(Converter<int, Foo>));
Foo foo = func(123);
string s = foo.ToString(); // proof
Я думаю, что такой краткий, как вы собираетесь получить (без перехода на заводской шаблон), будет что-то с анонимными методами, например:
delegate Foo FooGenerator(int x);
...
void DoStuff()
{
YourDelegateConsumer(x => new Foo(x));
}
Это не делает строго то, что вы просили (так как вы передаете делегат анонимному методу, который возвращает новый экземпляр, а не прямой делегат конструктору), но я не думаю, что то, что вы просите, строго возможно.
это, конечно, при условии, что вы используете 3.5+
к сожалению, нет, конструкторы не совсем то же самое, что методы, и как таковые вы не можете создать делегат, который указывает на них. Это интересная идея, хотя, возможно, с дополнительной информацией мы могли бы разработать какой-то обходной путь, который был бы синтаксически похож.
Марк Gravell вдохновил меня на следующее Очень простое решение:
static void Main()
{
Pet a = _MakeObject(typeof(Dog));
Pet b = _MakeObject(typeof(Cat));
}
private static Pet _MakeObject(Type type)
{
ConstructorInfo info = type.GetConstructor(new Type[0]);
return (Pet)info?.Invoke(null);
}
почти то же самое, если ваш конструктор имеет params (в этом примере: 1 param типа int):
static void Main()
{
Pet a = _MakeObject(typeof(Dog), 5);
Pet b = _MakeObject(typeof(Cat), 7);
}
private static Pet _MakeObject(Type type, int age)
{
ConstructorInfo info = type.GetConstructor(new [] { typeof(int) });
return (Pet)info?.Invoke(new object[] { age });
}