문제

정적 공장 방법이있는 수업이 있습니다. 공장에 전화하여 클래스 인스턴스를 검색 한 다음 C# Object Initializer 구문을 통해 선호하는 추가 초기화를 수행하고 싶습니다.

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

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;
도움이 되었습니까?

해결책

아니요. 또는 람다를 인수로 받아 들일 수 있으며, 이는 "창조 프로세스의 일부를 호출 할 수있는 모든 통제를 제공합니다. 이런 식으로 당신은 그것을 다음과 같이 부를 수 있습니다.

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

생성은 다음과 비슷하게 보일 것입니다.

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

또 다른 옵션은 대신 빌더를 반환하는 것입니다 (암시 적 캐스트 연산자와 함께 MyClass에). 당신이 원하는 것 :

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

확인하다 이것 건축업자를 위해

이 버전은 모두 컴파일 시간에 확인되며 전체 지능적인 지원이 있습니다.


기본 생성자가 필요한 세 번째 옵션 :

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

컴파일 시간에 점검되었고 확인되지 않은 중간입니다. 과제에 대한 지속적인 표현을 강요하기 때문에 약간의 작업이 필요합니다. 나는 다른 것이 이미 답에있는 접근법의 변형이라고 생각합니다. 일반 과제를 사용할 수도 있습니다. 실제로 필요한 경우 고려하십시오.

다른 팁

다음과 같은 확장 방법을 사용할 수 있습니다.

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

당신은 그것을 다음과 같이 부를 것입니다.

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

"아니오"에서 +1.

익명의 대상 방법에 대한 대안은 다음과 같습니다.

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

이 경우 FactoryCreate() 다음과 같습니다.

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

아니요, 객체 이니셜 라이저는 생성자와 함께 "새"호출에만 사용할 수 있습니다. 한 가지 옵션은 공장 내부의 객체 생성에서 해당 값을 설정하기 위해 공장 메소드에 추가 Args를 추가하는 것입니다.

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

모두가 말했듯이, 아니요.

논쟁으로서의 람다는 이미 제안되었다.
더 우아한 접근법은 익명을 받아들이고 객체에 따라 속성을 설정하는 것입니다. 즉

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

물체는 모든 속성에 대해 반영되어야하기 때문에 훨씬 느릴 것입니다.

아니, 그것은 당신이 '인라인'만 할 수있는 일입니다. 공장 기능이 귀하를 위해 할 수있는 모든 것은 참조를 반환하는 것입니다.

나에게 객체 이니셜 라이저의 킬러 기능은 나머지 초기화되지 않은 속성 목록을 보는 것입니다. 따라서 이미 생성 된 인스턴스에 실제로 객체 이니셜 라이저를 사용하는 방법이 있습니다. 간단한 객체 래퍼를 만들어야합니다.

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

    public TObject Obj { get; }
}

이제 이와 같이 사용하여 개체를 초기화 할 수 있습니다.

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top