Конструктор в качестве делегата - возможно ли это на С#?
У меня есть класс, как показано ниже:
class Foo
{
public Foo(int x) { ... }
}
и мне нужно передать определенному методу делегат следующим образом:
delegate Foo FooGenerator(int x);
Можно ли передать конструктор непосредственно как значение FooGenerator
, не вводя:
delegate(int x) { return new Foo(x); }
?
РЕДАКТИРОВАТЬ: Для моего личного использования вопрос относится к .NET 2.0, но приветствия/ответы для версии 3.0+ также приветствуются.
Ответы
Ответ 1
Нет, 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; });
Ответ 2
Я предполагаю, что вы обычно делаете что-то вроде этого как часть реализации factory, где фактические типы не известны во время компиляции...
Во-первых, обратите внимание, что более простой подход может быть шагом после создания, затем вы можете использовать generics:
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
Ответ 3
Я думаю, что сжатый, как вы собираетесь получить (без перехода к шаблону factory), будет что-то вроде анонимных методов, например:
delegate Foo FooGenerator(int x);
...
void DoStuff()
{
YourDelegateConsumer(x => new Foo(x));
}
Это не делает строго то, что вы просили (поскольку вы передаете делегат анонимному методу, который возвращает новый экземпляр, а не прямой делегат для конструктора), но я не думаю, просьба строго возможна.
Это, конечно, предполагается, что вы используете 3.5 +
Ответ 4
Похоже, вы, вероятно, хотите использовать шаблон класса factory.
Factory Шаблон метода
Ответ 5
К сожалению, конструкторы не совсем такие же, как методы, и поэтому вы не можете создать делегат, который указывает на них. Это интересная идея, хотя, возможно, с дополнительной информацией мы могли бы разработать какое-то обходное решение, которое будет синтаксически схожим.
Ответ 6
Ответ Марка Гравелла вдохновил меня на следующее очень простое решение:
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);
}
Почти то же самое, если ваш конструктор имеет параметры (в этом примере: 1 параметр типа 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 });
}
Ответ 7
Я предполагаю, что это невозможно, так как вы передадите метод объекта, который еще не был создан.
Ответ 8
Другой вариант - использовать класс Activator
, например:
Использование универсальных типов
public delegate Foo FooGeneratorDelegate<T>(int x);
public T FooGeneratorFunction<T>(int x)
{
return (T)Activator.CreateInstance(typeof(T), x);
}
// implementation example
FooGeneratorDelegate<Foo> del = FooGeneratorFunction<Foo>;
Foo foo = del(someIntValue);
Передача вашего типа Foo в качестве параметра
public delegate object FooGeneratorDelegate(Type t, int x);
public object FooGeneratorFunction(Type t, int x)
{
return Activator.CreateInstance(t, x);
}
// implementation example
FooGeneratorDelegate del = FooGeneratorFunction;
Foo foo = (Foo)del(typeof(Foo), someIntValue);
Если тип всегда будет иметь тип Foo
public delegate Foo FooGeneratorDelegate(int x);
public Foo FooGeneratorFunction(int x)
{
return (Foo)Activator.CreateInstance(typeof(Foo), x);
}
// implementation example
FooGeneratorDelegate del = FooGeneratorFunction;
Foo foo = del(someIntValue);