2009-03-23 5 views
24

私はそれに静的ファクトリメソッドを持つクラスを持っています。私はpreferablly C#のオブジェクト初期化構文を経由して、クラスのインスタンスを取得するために工場を呼び出して、その後、追加の初期化をしたい:ファクトリメソッドでC#オブジェクトイニシャライザを使用することはできますか?

MyClass instance = MyClass.FactoryCreate(); 
instance.someProperty = someValue; 
+0

は私が右の構文にやや近いラムダ・ソリューションを、好き – nawfal

答えて

25

いいえ。ラムダを引数として受け入れることもできます。これは、「作成」プロセスの一部が呼び出される完全な制御も提供します。

MyClass instance = MyClass.FactoryCreate(c=> 
    { 
     c.SomeProperty = something; 
     c.AnotherProperty = somethingElse; 
    }); 

作成はのようになります:あなたが好きそれを呼び出すことができますこの方法

public static MyClass FactoryCreate(Action<MyClass> initalizer) 
{ 
    MyClass myClass = new MyClass(); 
    //do stuff 
    initializer(myClass); 
    //do more stuff 
    return myClass; 
} 

別のオプションは、(MyClassのへの暗黙のキャスト演算子で)代わりにビルダーを返すことです。あなたはどちらを呼び出しますように:ビルダー

これらのバージョンの両方がコンパイル時にチェックし、完全なインテリセンスをサポートしているため

MyClass instance = MyClass.FactoryCreate() 
    .WithSomeProperty(something) 
    .WithAnotherProperty(somethingElse); 

チェックthis


デフォルトコンストラクタが必要です番目のオプション:

//used like: 
var data = MyClass.FactoryCreate(() => new Data 
{ 
    Desc = "something", 
    Id = 1 
}); 
//Implemented as: 
public static MyClass FactoryCreate(Expression<Func<MyClass>> initializer) 
{ 
    var myclass = new MyClass(); 
    ApplyInitializer(myclass, (MemberInitExpression)initializer.Body); 
    return myclass ; 
} 
//using this: 
static void ApplyInitializer(object instance, MemberInitExpression initalizer) 
{ 
    foreach (var bind in initalizer.Bindings.Cast<MemberAssignment>()) 
    { 
     var prop = (PropertyInfo)bind.Member; 
     var value = ((ConstantExpression)bind.Expression).Value; 
     prop.SetValue(instance, value, null); 
    } 
} 

そのコンパイル時にチェックし、チェックしていない間の中間。それは課題に一定の表現を強制するので、何らかの仕事が必要です。私は、答えにすでに入っているアプローチのバリエーションが他のものだと思います。通常の割り当てを使用することもできますが、実際にこれが必要かどうかを検討してください。

+0

:) C#は(彼らは、インスタンスのためにしたように、コレクションのために「追加」)は、このような方法を「作成」静的ためのいくつかの砂糖を追加望んだが、ビルダーの構文では、特に抽象的な工場状況では実行可能ではないすべてのプロパティに対して関数を作成する必要があります。 –

+0

@Gaijinラムダは本当に素早く素敵で、よくサポートされた方法です。私は、明確なデフォルトとプロパティを設定するだけではないいくつかの方法でテストコードのためのビルダーを使用します。特に、MyClassが(コンストラクターですべてを適用する必要があるため)不変の場合は、本当に便利です。 – eglasius

+0

@Gaijinは、普通の課題にも行くことを覚えているコメントを添えて、第3のバージョンを投稿しました。 – eglasius

1

MyClass instance = MyClass.FactoryCreate() 
{ 
    someProperty = someValue; 
} 

をなし、オブジェクト初期化子のみ可能コンストラクタで "new"の呼び出しで使用されます。 1つのオプションは、ファクトリメソッド内のオブジェクト作成時にそれらの値を設定するために、いくつかの追加のargsをファクトリメソッドに追加することです。

MyClass instance = MyClass.FactoryCreate(int someValue, string otherValue); 
0

いいえ、これは「インライン」でしかできないものです。すべてのファクトリ関数は参照を返すことができます。

1

誰もが言ったように、いいえ。

引数としてラムダが既に提案されています。
より洗練されたアプローチは、匿名を受け入れ、オブジェクトに応じてプロパティを設定することです。すなわち

MyClass instance = MyClass.FactoryCreate(new { 
    SomeProperty = someValue, 
    OtherProperty = otherValue 
}); 

オブジェクトはすべてのプロパティで反映される必要があるため、はるかに遅くなります。

2

+1「いいえ」です。ここで

は匿名オブジェクトの方法に代わるものです。この場合

var instance = MyClass.FactoryCreate(
    SomeProperty => "Some value", 
    OtherProperty => "Other value"); 

FactoryCreate()はのようになります。

public static MyClass FactoryCreate(params Func<object, object>[] initializers) 
{ 
    var result = new MyClass(); 
    foreach (var init in initializers) 
    { 
     var name = init.Method.GetParameters()[0].Name; 
     var value = init(null); 
     typeof(MyClass) 
      .GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase) 
      .SetValue(result, value, null); 
    } 
    return result; 
} 
+0

ニース!式>で同じことをして、リファクタリングの必要性を取り除くと、それは速くないでしょうか? – configurator

+2

自分自身に答える:いいえ、そうではありません。毎回式をコンパイル()する必要があります。ベンチマークでは、30倍以上遅いと言われています... – configurator

3

あなたは次のような拡張メソッドを使用することができます。

namespace Utility.Extensions 
{ 
    public static class Generic 
    { 
     /// <summary> 
     /// Initialize instance. 
     /// </summary> 
     public static T Initialize<T>(this T instance, Action<T> initializer) 
     { 
      initializer(instance); 
      return instance; 
     } 
    } 
} 

あなたは次のように呼びます:

using Utility.Extensions; 
// ... 
var result = MyClass.FactoryCreate() 
       .Initialize(x => 
       { 
        x.someProperty = someValue; 
        x.someProperty2 = someValue2; 
       }); 
関連する問題