Question

J'ai une classe avec une méthode de fabrication statique sur elle. Je veux appeler l'usine pour récupérer une instance de la classe, puis effectuez une autre initialisation, preferablly via c # syntaxe d'initialisation:

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

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
Était-ce utile?

La solution

Non. Sinon, vous pouvez accepter un lambda comme argument, ce qui vous donne également le plein contrôle dans lequel une partie du processus « de création » sera appelée. De cette façon, vous pouvez l'appeler comme:

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

La création ressemblerait à:

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

Une autre option est de retourner un constructeur à la place (avec un opérateur de cast implicite à MyClass). Ce qui vous appelleriez comme:

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

Vérifier cette pour le constructeur

Ces deux versions sont vérifiées au moment de la compilation et un support complet IntelliSense.


Une troisième option qui nécessite un constructeur par défaut:

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

Son un milieu entre vérifié au moment de la compilation et non vérifiées. Il a besoin un peu de travail, car il est l'expression constante force sur les missions. Je pense que toute autre chose des variations des approches déjà dans les réponses. Rappelez-vous que vous pouvez également utiliser les fonctions habituelles, considérer si vous avez vraiment besoin de tout cela.

Autres conseils

Vous pouvez utiliser une méthode d'extension, comme suit:

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

Vous appelez comme suit:

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

1 sur "Non".

Voici une alternative à la façon d'objet anonyme:

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

Dans ce cas, serait quelque chose FactoryCreate() comme:

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

Non, le initialiseur d'objet ne peut être utilisé sur un appel à « nouveau » avec le constructeur. Une option pourrait être d'ajouter quelques args supplémentaires à votre méthode d'usine, pour définir ces valeurs à la création d'objets à l'intérieur de l'usine.

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

Comme tout le monde a dit, non.

Un lambda comme argument a déjà été suggéré.
serait une approche plus élégante d'accepter un anonyme et définissez les propriétés en fonction de l'objet. i.e..

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

Ce serait beaucoup plus lent que, puisque l'objet devrait être réfléchi pour toutes les propriétés.

Non, c'est quelque chose que vous ne pouvez le faire « en ligne ». Toutes les fonctions d'usine peut faire pour vous est de retourner une référence.

Pour moi, la fonction de tueur pour initialiseurs d'objet est de voir la liste des propriétés restantes non initialisées. Voici donc une autre astuce comment utiliser effectivement initialiseur d'objet, par exemple, déjà créé. Vous devez créer un wrapper objet simple:

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

    public TObject Obj { get; }
}

Et maintenant vous pouvez l'utiliser comme ceci pour initialiser vos objets:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top