web-dev-qa-db-ja.com

デリゲートとしてのコンストラクタ-C#では可能ですか?

以下のようなクラスがあります:

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

そして、私はこのようなデリゲートを特定のメソッドに渡す必要があります:

delegate Foo FooGenerator(int x);

次のように入力せずに、コンストラクタをFooGenerator値として直接渡すことは可能ですか?

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

編集:私の個人的な使用では、質問は.NET 2.0に言及していますが、3.0以降のヒント/応答も歓迎します。

55
akavel

いいえ、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; });
28
leppie

私は通常、ファクトリー実装の一部としてこのようなことを行うと想定していますが、実際の型はコンパイル時には不明です...

最初に、より簡単なアプローチは作成後の初期化ステップである場合があることに注意してください。次にジェネリックを使用できます。

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

その後、MakeGenericMethodCreateDelegateを使用できます。


さもないと; 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
54
Marc Gravell

(ファクトリーパターンに移動せずに)取得するのと同じくらい簡潔な方法は、次のような匿名メソッドを使用したものになると思います。

delegate Foo FooGenerator(int x);

...    

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

これはあなたが要求したことを厳密に行っていません(コンストラクタに直接デリゲートするのではなく、新しいインスタンスを返す匿名メソッドにデリゲートを渡しているため)が、私はあなたが求めていることを考えていません厳密に可能です。

もちろん、これは3.5以上を使用していることを前提としています

7
Adam Robinson

おそらくクラスファクトリパターンを使用したいようです。

ファクトリメソッドパターン

5
Mongus Pong

残念ながら、コンストラクタはメソッドとまったく同じではないため、それらを指すデリゲートを作成することはできません。これは興味深いアイデアですが、おそらくより多くの情報があれば、構文的に類似した何らかの回避策を考案できます。

2
Andrew Hare

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

コンストラクターにパラメーターがある場合もほぼ同じです(この例では、int型の1つのパラメーター)。

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
Kim Homann

まだ作成されていないオブジェクトのメソッドを渡すので、それは不可能だと思います。

0
Serge Wautier