Est-il possible d'utiliser un initialiseur d'objet c# avec une méthode d'usine?

j'ai une classe statique méthode de fabrique. Je veux appeler l'usine pour récupérer une instance de la classe, puis faire une initialisation supplémentaire, préférablement via la syntaxe C # object initializer:

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

et

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
24
demandé sur Jason Coyne 2009-03-24 01:55:54
la source

6 ответов

Non. Vous pouvez aussi accepter un lambda comme argument, ce qui vous donne aussi le contrôle total dans quelle 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;
   });

créer devrait ressembler à:

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 builder à la place (avec un opérateur implicite de cast à MyClass). Que vous appelleriez comme:

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

Case pour les builder

ces deux versions sont vérifiées au moment de la compilation et ont le support intellisense complet.


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

C'est un intermédiaire entre vérifié au moment de la compilation et non vérifié. Il a besoin de travail, car il force l'expression constante sur les missions. Je pense que tout le reste est une variation des approches déjà présentées dans les réponses. N'oubliez pas que vous pouvez également utiliser la normale affectations, pensez si vous avez vraiment besoin de tout cela.

26
répondu eglasius 2009-03-24 22:36:46
la source

Vous pouvez utiliser une méthode d'extension telle que la suivante:

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 l'appelez comme suit:

using Utility.Extensions;
// ...
var result = MyClass.FactoryCreate()
                .Initialize(x =>
                {
                    x.someProperty = someValue;
                    x.someProperty2 = someValue2;
                });
3
répondu Hans Vonn 2017-04-17 19:45:06
la source

+1 sur "Non".

Voici une alternative à l'objet anonyme façon:

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

Dans ce cas FactoryCreate() serait quelque chose 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;
}
2
répondu Paul Stovell 2009-03-24 02:30:23
la source

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

MyClass instance = MyClass.FactoryCreate(int someValue, string otherValue);
1
répondu Andy White 2009-03-24 01:57:35
la source

comme tout le monde l'a dit, non.

un lambda comme argument a déjà été suggéré.

Une approche plus élégante serait d'accepter un anonyme et de définir 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, mais, depuis l'objet devra être répercuté sur toutes les propriétés.

1
répondu configurator 2009-03-24 02:12:28
la source

Non, c'est quelque chose qu'on ne peut faire qu'en ligne. Tous la fonction de fabrication peut faire pour vous est de retourner une référence.

0
répondu Trap 2009-03-24 01:58:57
la source

Autres questions sur