Pergunta

Eu tenho uma classe como a seguir:

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

e eu preciso passar a um determinado método de um delegado como esta:

delegate Foo FooGenerator(int x);

É possível passar o construtor diretamente como um valor FooGenerator, sem ter que digitar:

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

?

EDIT:. Para o meu uso pessoal, a questão refere-se ao .NET 2.0, mas sugestões / respostas para 3.0+ são bem-vindas

Foi útil?

Solução

Não, o CLR não permite que delegados ligação a ConstructorInfo.

No entanto, pode apenas criar o seu próprio:

static T Make<T>(Action<T> init) where T : new()
{
  var t = new T();
  init(t);
  return t;
}

Uso

var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });

Outras dicas

Eu estou supondo que você faria normalmente algo como isso como parte de uma implementação de fábrica, onde os tipos reais não são conhecidos em tempo de compilação ...

Em primeiro lugar, nota que uma abordagem mais fácil pode ser um pós-criar etapa de inicialização, então você pode usar os genéricos:

static T Create<T>({args}) where T : class, ISomeInitInterface, new() {
    T t = new T();
    t.Init(args);
    return t;
}

Você pode então usar MakeGenericMethod e / ou CreateDelegate.


Caso contrário; você pode fazer isso com na mosca com Expression (3.5) ou DynamicMethod (2,0).

A abordagem Expression é mais fácil de código:

    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

ou (usando 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

Eu acho que o mais conciso como você está indo para obter (sem mover a um padrão de fábrica) seria algo com métodos anônimos, como este:

delegate Foo FooGenerator(int x);

...    

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

Isto não está fazendo estritamente o que você pediu (uma vez que você está passando um delegado para um método anônimo que retorna uma nova instância, ao invés de um delegado direto para o construtor), mas eu não acho que você' está pedindo é estritamente possível.

Este é, naturalmente, supondo que você está usando 3,5 +

Parece que você provavelmente vai querer estar usando o padrão de fábrica de classe.

Factory Method Padrão

Infelizmente não, construtores não são exatamente as mesmas coisas que os métodos e, como tal, você não pode criar um delegado que aponta para eles. Esta é uma idéia interessante, porém, talvez com mais informações poderíamos conceber algum tipo de solução que seria sintaticamente similar.

A resposta de Marc Gravell me inspirou a solução muito simples seguinte:

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);
}

Quase a mesma coisa se o seu construtor tem parâmetros (neste exemplo: 1 param de tipo 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 });
}

Meu palpite é que ele não é possível desde que você iria passar um método de um objeto que não foi criado ainda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top