Frage

Ich habe eine Klasse mit einer statischen Fabrikmethode. Ich möchte die Fabrik aufrufen, um eine Instanz der Klasse abzurufen, und dann zusätzliche Initialisierung durchzuführen, bevorzugt über C# Object Initializer -Syntax:

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

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
War es hilfreich?

Lösung

Nein. Alternativ können Sie eine Lambda als Argument akzeptieren, die Ihnen auch die volle Kontrolle über welchen Teil des "Schöpfungsprozesses" aufgerufen wird. Auf diese Weise können Sie es so nennen wie:

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

Das Erstellen würde ähnlich aussehen wie mit:

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

Eine andere Möglichkeit besteht darin, stattdessen einen Bauunternehmer zurückzugeben (mit einem impliziten Gussbetreiber an MyClass). Was du wie nennen würdest:

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

Prüfen Dies Für den Baumeister

Beide Versionen werden zur Kompilierungszeit überprüft und haben vollständige IntelliSense -Unterstützung.


Eine dritte Option, die einen Standardkonstruktor erfordert:

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

Es ist eine Mitte zwischen der Kompilierungszeit und nicht überprüft. Es braucht etwas Arbeit, da es den Aufgaben einen ständigen Ausdruck erzwingt. Ich denke, dass alles andere Variationen der Ansätze sind, die bereits in den Antworten sind. Denken Sie daran, dass Sie auch die normalen Aufgaben verwenden können. Überlegen Sie, ob Sie eines davon wirklich benötigen.

Andere Tipps

Sie können eine Erweiterungsmethode wie die folgenden verwenden:

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

Sie würden es wie folgt nennen:

using Utility.Extensions;
// ...
var result = MyClass.FactoryCreate()
                .Initialize(x =>
                {
                    x.someProperty = someValue;
                    x.someProperty2 = someValue2;
                });

+1 auf "nein".

Hier ist eine Alternative zum anonymen Objektweg:

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

In diesem Fall FactoryCreate() wäre so etwas wie:

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

Nein, der Objektinitialisierer kann nur mit dem Konstruktor "neu" verwendet werden. Eine Option könnte darin bestehen, Ihrer Werksmethode einige zusätzliche Argumente hinzuzufügen und diese Werte auf die Objekterstellung in der Fabrik festzulegen.

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

Wie alle sagten, nein.

Ein Lambda als Argument wurde bereits vorgeschlagen.
Ein eleganterer Ansatz wäre, ein Anonymous zu akzeptieren und die Eigenschaften nach dem Objekt festzulegen. dh

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

Das wäre jedoch viel langsamer, da sich das Objekt für alle Eigenschaften widerspiegeln müsste.

Nein, das ist etwas, das Sie nur "Inline" tun können. Alles, was die Fabrikfunktion für Sie erledigen kann, besteht darin, eine Referenz zurückzugeben.

Für mich ist die Killer -Funktion für Objektinitialisierer die Liste der verbleibenden nicht initialisierten Eigenschaften. Hier ist ein weiterer Trick, wie Sie den Objektinitializer für bereits erstellte Instanz verwenden können. Sie sollten eine einfache Objektverpackung erstellen:

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

    public TObject Obj { get; }
}

Und jetzt können Sie es so verwenden, um Ihre Objekte zu initialisieren:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top