Pergunta

Eu tenho uma aula com um método estático de fábrica. Quero ligar para a fábrica para recuperar uma instância da classe e, em seguida, faça a inicialização adicional, preferível via sintaxe do inicializador do objeto C#:

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

vs.

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

Solução

Não. Alternativamente, você pode aceitar um lambda como um argumento, o que também fornece controle total em qual parte do processo de "criação" será chamada. Dessa forma, você pode chamá -lo como:

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

A criação seria semelhante a:

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

Outra opção é retornar um construtor (com um operador de elenco implícito na MyClass). Que você chamaria como:

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

Verificar isto para o construtor

Ambas as versões são verificadas no horário de compilação e têm total apoio do Intellisense.


Uma terceira opção que requer um construtor padrão:

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

É um meio entre verificado no horário de compilação e não é verificado. Ele precisa de algum trabalho, pois está forçando a expressão constante nas tarefas. Eu acho que qualquer outra coisa são variações das abordagens já nas respostas. Lembre -se de que você também pode usar as atribuições normais, considere se você realmente precisar disso.

Outras dicas

Você pode usar um método de extensão como o seguinte:

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

Você chamaria o seguinte:

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

+1 em "não".

Aqui está uma alternativa à maneira do objeto anônimo:

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

Nesse caso FactoryCreate() seria algo 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;
}

Não, o inicializador do objeto só pode ser usado em uma chamada para "novo" com o construtor. Uma opção pode ser adicionar alguns args adicionais ao seu método de fábrica, para definir esses valores na criação de objetos dentro da fábrica.

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

Como todos disseram, não.

Um lambda como argumento já foi sugerido.
Uma abordagem mais elegante seria aceitar um anônimo e definir as propriedades de acordo com o objeto. ou seja

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

Isso seria muito mais lento, já que o objeto teria que ser refletido para todas as propriedades.

Não, isso é algo que você só pode fazer 'inline'. Toda a função de fábrica pode fazer por você é retornar uma referência.

Para mim, o recurso assassino dos inicializadores de objetos é ver a lista das propriedades não inicializadas restantes. Então, aqui está outro truque de como fazer realmente usar o inicializador de objetos para instância já criada. Você deve criar um invólucro de objeto simples:

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

    public TObject Obj { get; }
}

E agora você pode usá -lo assim para inicializar seus objetos:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top