конструктор как делегат - возможно ли это в C #? - PullRequest
54 голосов
/ 21 октября 2009

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

class Foo
{
  public Foo(int x) { ... }
}

и мне нужно передать определенному методу делегат, подобный этому:

delegate Foo FooGenerator(int x);

Можно ли передать конструктор непосредственно как значение FooGenerator, не вводя:

delegate(int x) { return new Foo(x); }

РЕДАКТИРОВАТЬ: Для моего личного использования вопрос относится к .NET 2.0, но приветствуются также подсказки / ответы для 3.0+.

Ответы [ 7 ]

53 голосов
/ 21 октября 2009

Я предполагаю, что вы обычно делаете что-то подобное в рамках фабричной реализации, где фактические типы не известны во время компиляции ...

Во-первых, обратите внимание, что более простым подходом может быть этап инициализации после создания, затем вы можете использовать обобщенные значения:

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
27 голосов
/ 21 октября 2009

Нет, 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; });
7 голосов
/ 21 октября 2009

Я думаю, что кратким (без перехода к заводскому шаблону) было бы что-то с анонимными методами, например:

delegate Foo FooGenerator(int x);

...    

void DoStuff()
{
    YourDelegateConsumer(x => new Foo(x));
}

Это не совсем то, что вы просили (поскольку вы передаете делегата анонимному методу, который возвращает новый экземпляр, а не прямой делегат конструктору), но я не думаю, что вы ' Перепрошивать строго возможно.

Это, конечно, при условии, что вы используете 3,5 +

5 голосов
/ 21 октября 2009

Похоже, вы, вероятно, хотите использовать шаблон фабрики классов.

Шаблон фабричного метода

2 голосов
/ 11 января 2016

Ответ Марка Гравелла вдохновил меня на следующее очень простое решение:

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 });
}
2 голосов
/ 21 октября 2009

К сожалению, нет, конструкторы - это не совсем то же самое, что методы, и поэтому вы не можете создать делегат, который указывает на них. Это интересная идея, хотя, возможно, с большим количеством информации мы могли бы разработать какой-то обходной путь, который был бы синтаксически похожим.

0 голосов
/ 21 октября 2009

Я предполагаю, что это невозможно, поскольку вы передадите метод объекта, который еще не был создан.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...