2012-02-15 4 views
2

내 사이트에서 싱글 톤 객체로 인스턴스화되는 DataAdapter 클래스가 있습니다. 그 클래스는 내가 게으른 싱글이 될 싶은 사람 속성이 코드는이 작업을 수행합니다 :. 싱글 톤 속성의 .NET 게으른 초기화

private readonly object _personLock = new object(); 
private volatile IPersonManager _person; 
public IPersonManager Person 
{ 
    get 
    { 
     if (_person == null) 
     { 
      lock (_personLock) 
      { 
       if (_person == null) 
       { 
        _person = new PersonManager(_adUserName, _adPassword, client); 
       } 
      } 
     } 
     return _person; 
    } 
} 

합니다 (PersonManager 생성자에 그 세 인수는 속성/현재 개체의 필드입니다.) 이 코드는 완벽하게 작동합니다 (double-lock check 패턴).

그러나 이것은 많은 코드입니다. 더 간단하게 만들기 위해 .Net 4.0에서 새로운 Lazy<> type을 사용하고 싶습니다. 그래서 코드를 변경합니다 :

private static readonly Lazy<IPersonManager> _person = new Lazy<IPersonManager>(() => new PersonManager(_adUserName, _adPassword, client)); 
    public static IPersonManager Person { get { return _person.Value; } } 

그러나이 세 매개 변수는 정적이 아니기 때문에 (현재 메서드의 인스턴스 개체이기 때문에) 작동하지 않습니다. writeupsI've found 중 아무 것도이를 처리하지 않습니다. 그 값을 람다 식으로 전달하는 방법이 필요합니까? Lazy <> 클래스는 빈 서명을 기대하는 것처럼 보입니다.

답변

0

합니다 (PersonManager 생성자에 그 세 인수는 현재 오브젝트에 속성/필드입니다.) 생성자는 개체를 초기화하는 데 사용됩니다

. 전달하려는 인수에는이 시점에서 할당 된 값이 없습니다. 개체에 이러한 값을 제대로 초기화해야하는 경우 초기화 할 때 전달해야합니다.

속성을 메서드로 변환하고 이러한 값을 정적 GetInstance 메서드에 전달할 수는 있지만 처음으로 GetInstance가 호출 될 때만이 값을 한 번 설정합니다. 이것은 아마도 좋은 생각이 아니지만 할 수 있습니다. Person 속성을 Method로 변환하고 이러한 매개 변수를 받아들이고 생성자를 초기화하는 데 사용합니다. 이는 Lazy<T>을 사용하지 않으며 코드 줄이 늘어날 것이지만 훨씬 더 예측 가능한 동작을 의미합니다.

+0

당신은 값을 갖는, 그들은 개체의 생성자 내에서 값을 할당하고하지 개체에 대한 잘못된입니다. null 참조 예외가 발생하지 않고 해당 필드를 찾을 수 없기 때문에 컴파일 오류가 발생합니다. 내가 제안한 내 (작업) 이중 확인 잠금 솔루션보다 당신이 무엇을 제안하고 있는지 잘 모르겠습니다. – Arbiter

+0

@Arbiter 코드에서 초기화하는 것이 명확하지 않습니다. KeithS는 당신을 위해 일해야한다고 제안합니다. 나는 패턴을 섞지 말라고 제안했다. – sarvesh

3

글쎄, Lazy가 인스턴스 속성을 사용하여 인스턴스에 인스턴스를 사용하여 속성을 제공한다면 어떻게 될까요? 우리가 클래스 내부에서 영리한 작업을하기 때문에이 필드는 여전히 비공개가 될 수 있으며, 실행 중에는 Singleton.Instance가 처음 참조 될 때까지 전체 필드가 ​​여전히 게으르다. 그러나 코드가 Person 속성을 가져 오기 전에 개인 필드가 적절한 값을 가져야합니다. Singleton이 인스턴스를 생성 할 때 열심히로드되면 괜찮습니다.

C# In Depth에서 빌려온 여기에 싱글 톤을 참조하는 람다 (lambda)를 사용하여 초기화 된 완전한 게으른 Person 멤버가있는 유사 게으른 싱글 톤이 있습니다.

public sealed class Singleton 
{ 
    private static readonly Singleton instance = new Singleton(); 

    // Explicit static constructor to tell C# compiler 
    // not to mark type as beforefieldinit 
    static Singleton() 
    { 
    } 

    private Singleton() 
    { 
     //I HIGHLY recommend you initialize _adusername, 
     //_adpassword and client here. 
    } 

    public static Singleton Instance 
    { 
     get 
     { 
      return instance; 
     } 
    } 

    private static readonly Lazy<IPersonManager> _person = 
     new Lazy<IPersonManager>(() => new PersonManager(Instance._adUserName, Instance._adPassword, Instance.client)); 
    public static IPersonManager Person { get { return _person.Value; } } 

    private object _adUserName; 
    private object _adPassword; 
    private object client; 
} 

public class PersonManager:IPersonManager {} 

public interface IPersonManager{} 

편집 : 당신은 IOC의가있는 경우는 IOC의를 사용합니다. 현재 패턴을 혼합하려고합니다. 당신은 런타임 규칙을 사용하여 인스턴스 클래스를 싱글 톤으로 "승격"하기 위해 IoC를 사용하지만이 인위적인 싱글 톤의 인스턴스 스코프 된 데이터 필드를 기반으로 컴파일러가 적용한 지연된 정적 속성을 인스턴스화하려고합니다. 이것은 단순히 작동하지 않을 것입니다.

일단 IoC를 실행하면 모든 종속성을 등록하고 주입해야합니다. PersonManager에 Ninject를 IPersonManager 구현으로 등록한 다음 IPersonManager를 생성하는 Func을 제공 할 수있는 기본 Singleton DataAdapter에 대한 생성자를 만듭니다.일반적으로이 목적을 위해 사용자 지정 함수를 정의 할 수 있습니다.이 경우 IoC를 사용하여 컨테이너에 보관 된 단일 DataAdapter 인스턴스에서 필요한 인스턴스 데이터를 제공합니다.

경고 : 심각하게 추악한 반사를 피하기 위해 데이터 필드를 공개적으로 읽을 수 있어야합니다. 필드를 읽기 전용 필드 또는 get 전용 속성으로 정의하여 사람들이 변경하지 못하게하지만 소비자가 볼 수있게 할 수 있습니다.

편집 2 :가 여기에 내가 생각했던 내용은 다음과 같습니다

//in your Ninject bindings: 
kernel.Bind<DataAdapter>().ToSelf().InSingletonScope(); 
kernel.Bind<PersonManager>().ToSelf().InSingletonScope(); 
//to bind the interface 
kernel.Bind<IPersonManager>() 
    .ToMethod(c =>{ 
     var adapter = kernel.Get<DataAdapter>(); 
     //this is why these fields would have to be public 
     var arg1 = new ConstructorArgument("adUserName", adapter._adUserName) 
     var arg2 = new ConstructorArgument("adPassword", adapter._adPassword) 
     var arg3 = new ConstructorArgument("client", adapter.client) 
     //the names of the arguments must match PersonManager's constructor 
     c.Kernel.Get<PersonManager>(arg1, arg2, arg3); 
    }); 

//now in your DataAdapter, specify a constructor like this, and Ninject will provide: 

public DataAdapter(Func<IPersonManager> personFunc) 
{ 
    //_person should obviously not be instantiated where it's defined in this case 
    _person = new Lazy<IPersonManager>(personFunc); 
} 
+0

좋은 생각이지만 나에게는 도움이되지 않습니다. 나는이 같은 정적 속성을 사용하여'Singleton'을 구현하지 않고 있으며, 싱글 톤으로 ninject에 의해 주입되고 있습니다. 이 경우 싱글턴이 싱글 톤이 아니라 정규 인스턴스 멤버라면 어떻게 할 것입니까? – Arbiter

+0

@Arbiter : 편집을 참조하십시오. – KeithS

+0

'Singleton'은 현재 생성자를 가지고 있습니다 ('client' 객체는 연결 문자열 없이는 초기화 될 수 없습니다). 만약 생성자를 없애면 생성자 주입 대신 프로퍼티 인젝션을 사용하도록 인젝션을 수정해야합니다. 많은 게으름 뱅이가 게으른 <> 클래스를 사용하기 위해 뛰어 넘습니다. 나는 이것이 이것을위한 올바른 도구가 아니라고 생각하고 있습니다. – Arbiter