それは、ファクトリメソッドとC#のオブジェクト初期化子を使用することは可能ですか?

StackOverflow https://stackoverflow.com/questions/675545

質問

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

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

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
役に立ちましたか?

解決

はありません。また、あなたはまた、あなたの「作成」プロセスの一部が呼び出されますここで完全に制御することができます引数としてラムダを受け入れることができます。あなたはそれを呼び出すことができますこの方法が好きます:

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

ビルダーのためにこのに確認してください。

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

<時間>

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

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

コンパイル時にチェックし、チェックしていない間にその真ん中。それは割り当てに定数式を強制されるように、それは、いくつかの作業を必要としません。私は何か他のものが既に答えでのアプローチのバリエーションだと思います。あなたが本当にこののいずれかが必要な場合を検討し、あなたはまた、通常の割り当てを使用することができることを覚えておいてください。

他のヒント

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

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

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

いいえ、オブジェクト初期化子はコンストラクタと「新」への呼び出しで使用することができます。 1つのオプションは、工場内でのオブジェクトの作成時にこれらの値を設定するために、あなたのファクトリメソッドにいくつかの追加の引数を追加するかもしれません。

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

誰が言ったように、ノーます。

引数としてラムダは、すでに示唆されている。
よりエレガントなアプローチは、匿名を受け入れ、目的に応じてプロパティを設定することであろう。すなわちます。

MyClass instance = MyClass.FactoryCreate(new {
    SomeProperty = someValue,
    OtherProperty = otherValue
});
オブジェクトは、すべてのプロパティのために反映されなければならないので、

すなわち、しかしはるかに遅くなります。

いいえ、それはあなたが唯一の「インライン」を行うことができるものです。機能があなたのために何ができるすべての工場は、参照を返すことです。

私にはオブジェクト初期化子のためのキラー機能は残っている初期化されていないプロパティの一覧を確認することです。そこでここでは、実際に作るすでに作成されたインスタンスのオブジェクト初期化子を使用するには、どのように別のトリックがあります。あなたは、単純なオブジェクトラッパーを作成する必要があります:

public struct ObjectIniter<TObject>
{
    public ObjectIniter(TObject obj)
    {
        Obj = obj;
    }

    public TObject Obj { get; }
}

そして今、あなたのオブジェクトを初期化するために、このようにそれを使用することができます:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top