Domanda

Ho una classe con un metodo factory statica su di esso. Voglio chiamare la fabbrica per recuperare un'istanza della classe, e poi fare l'inizializzazione aggiuntivo, preferablly attraverso la sintassi di inizializzazione c # oggetto:

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

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
È stato utile?

Soluzione

No. In alternativa si potrebbe accettare una lambda come argomento, che vi dà anche il pieno controllo in cui sarà chiamato parte del processo di "creazione". In questo modo si può chiamare come:

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

Il creare apparirebbe simile a:

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

Un'altra opzione è quella di restituire un costruttore, invece (con un operatore cast implicito a MyClass). Che si sarebbe chiamata come:

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

questo per il costruttore

Entrambe le versioni vengono controllati in fase di compilazione e di avere il supporto Intellisense completo.


Una terza opzione che richiede un costruttore di default:

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

E 'un mezzo tra verificata al momento della compilazione e non controllato. Essa ha bisogno di un certo lavoro, come sta forzando l'espressione costante sulle assegnazioni. Penso che qualsiasi altra cosa sono variazioni degli approcci già nelle risposte. Ricordate che è possibile utilizzare anche le assegnazioni normali, prendere in considerazione se si ha realmente bisogno di tutto questo.

Altri suggerimenti

È possibile utilizzare un metodo di estensione come il seguente:

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

Si potrebbe chiamarla come segue:

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

+1 su "No".

Ecco un'alternativa al modo oggetto anonimo:

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

In questo caso FactoryCreate() sarebbe qualcosa come:

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

No, l'inizializzatore oggetto può essere utilizzato solo su una chiamata a "nuovo" con il costruttore. Una possibilità potrebbe essere quella di aggiungere alcuni argomenti aggiuntivi per il metodo di fabbrica, impostare questi valori a creazione di oggetti all'interno della fabbrica.

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

Come tutti ha detto, no.

Un lambda come un argomento è già stato suggerito.
Un approccio più elegante sarebbe quella di accettare un anonimo e impostare le proprietà in base all'oggetto. cioè.

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

Sarebbe molto più lento però, dato che l'oggetto dovrebbe essere riflessa via per tutte le proprietà.

No, è qualcosa che si può fare solo 'inline'. Tutte le funzioni fabbrica può fare per voi è quello di restituire un riferimento.

Per me la funzione di killer per inizializzatori oggetto è quello di vedere la lista dei restanti proprietà non inizializzate. Così qui è un altro trucco come fare effettivamente uso di inizializzazione degli oggetti, per esempio già creato. Si dovrebbe creare un semplice involucro oggetto:

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

    public TObject Obj { get; }
}

E ora si può usare in questo modo per inizializzare gli oggetti:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top