Pregunta

Tengo una clase con un método de fábrica estática en él. Quiero llamar a la fábrica para recuperar una instancia de la clase, y luego hacer la inicialización adicional, preferablly través de la sintaxis de C # inicializador de objeto:

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

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
¿Fue útil?

Solución

No. Como alternativa puede aceptar una lambda como un argumento, que también le da un control completo en el que se llamará parte del proceso de "creación". De esta manera se le puede llamar como:

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

La creación se vería similar a:

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

Otra opción es volver a un constructor en su lugar (con un operador de conversión implícita a MiClase). Que podríamos llamar como:

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

este para el constructor

Ambas versiones se comprueban en tiempo de compilación y tienen compatibilidad con IntelliSense completa.


Una tercera opción que requiere un constructor por defecto:

//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 un medio entre verificado en tiempo de compilación y no comprobadas. Se necesita algo de trabajo, ya que está obligando expresión constante en las tareas. Creo que todo lo demás son variaciones de los enfoques que ya están en las respuestas. Recuerde que también puede utilizar las asignaciones normales, considere si realmente necesita nada de esto.

Otros consejos

Puede utilizar un método de extensión tales como los siguientes:

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

Se podría llamar de la siguiente manera:

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

1 en "No".

Esta es una alternativa a la forma en objeto anónimo:

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

En este caso FactoryCreate() sería algo así como:

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, el inicializador de objeto sólo puede ser utilizado en una llamada a la "nueva" con el constructor. Una opción podría ser la de añadir algunos argumentos adicionales a su método de fábrica, para establecer estos valores en la creación de objetos dentro de la fábrica.

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

Al igual que todo el mundo dice, no.

Una lambda como un argumento ya se ha sugerido.
Un enfoque más elegante sería aceptar un anónimo y establecer las propiedades según el objeto. es decir.

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

Eso sería mucho más lento, sin embargo, ya que el objeto tendría que reflejarse en las propiedades para todos.

No, eso es algo que sólo se puede hacer 'en línea'. Toda la función de fábrica puede hacer por usted es para devolver una referencia.

Para mí la función única de inicializadores de objeto es ver la lista de propiedades que queda sin inicializar. Así que aquí es otro truco de cómo hacer realmente utilizan inicializador de objeto, por ejemplo, ya creado. Debe crear un simple envoltorio objeto:

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

    public TObject Obj { get; }
}

Y ahora se puede utilizar de esta manera para inicializar los objetos:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top