Конструктор в качестве делегата - возможно ли это на С#?

У меня есть класс, как показано ниже:

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 +

Ответ 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);