私のクラスInitializeメソッドが一度だけ呼び出されるようにするにはどうすればよいでしょうか?

私は現在Unity IoC Containerを使用しています。ここには私のAppConfigクラスがあります。あなたが見ることができるようにInitializeメソッドは一度だけ呼び出さなければならず、私は二重ロックチェックを使ってそれを保証しています。

私のアプローチが最良の方法でない場合、これを達成するための最良の方法は何でしょうか?

public interface IAppConfig
{
    /// 
/// Gets the admin username. ///
 
    /// The admin username.
    string AdminUsername { get; }

    /// 
/// Gets the admin password. ///
 
    /// The admin password.
    string AdminPassword { get; }

    /// 
/// Initializes this instance. ///
 
    void Initialize();
}

/// 
/// A singleton App config which helps reading from web.config /// its lifetime is controlled by Unity. ///
 
public class AppConfig : IAppConfig
{
    #region Fields

    /// 
/// the injectable config manager ///
 
    private readonly IConfigManager _configManager;

    private readonly ILogger _logger;

    private static readonly object LockObject = new object();

    private static bool _initialized = false;

    #endregion

    #region Constructors

    /// 
/// Initializes a new instance of the class. ///
 
    public AppConfig(IConfigManager configManager, ILogger logger)
    {
        this._configManager = configManager;
        this._logger = logger;
    }

    #endregion

    #region Properties

    /// 
/// Gets the admin username. ///
 
    /// The admin username.
    public string AdminUsername { get; private set; }

    /// 
/// Gets the admin password. ///
 
    /// The admin password.
    public string AdminPassword { get; private set; }

    #endregion

    #region Methods

    public void Initialize()
    {
        if (_initialized)
        {
            throw new ApplicationException("Initialize method should be called only once");
        }

        lock(LockObject)
        {
            if (_initialized) return;

            var adminUserNameSetting = _configManager.AppSettings[ConfigKeys.AdminUsername];

            if (adminUserNameSetting == null)
            {
                throw new ApplicationException("AdminUsername key not found");
            }

            this.AdminUsername = adminUserNameSetting.Value;

            if (String.IsNullOrWhiteSpace(this.AdminUsername))
            {
                _logger.LogError("AdminUsername not found");
            }

           //log
            var adminPasswordSetting = _configManager.AppSettings[ConfigKeys.AdminPassword];

            if (adminPasswordSetting == null)
            {
                throw new ApplicationException("AdminPassword key not found");
            }

            this.AdminPassword = adminPasswordSetting.Value;

            if (String.IsNullOrWhiteSpace(this.AdminPassword))
            {
                _logger.LogError("AdminPassword not found");
            }
            _initialized = true;
        }
    }

    #endregion
}

Unityでは、私は以下のコードを使用しています:

// IAppConfig
container.RegisterType(new ContainerControlledLifetimeManager(),
                                              new InjectionConstructor(configManager,
                                                                       logger));
var appConfig = container.Resolve();
appConfig.Initialize();
0
私はそれを一度呼びますが、間違って一度だけ呼び出され、二度も呼ばれないようにしたいと思います!
追加された 著者 The Light,
コンストラクタから呼び出す際の問題は、ユニットテスト可能な単位が少なくなることです。
追加された 著者 The Light,
なぜあなたは一度だけそれを呼び出さないのですか?トップスーパーの防御的なプログラミングよりも、このスーパーには何がありますか?
追加された 著者 Raynos,
ソースコードの静的解析は、ロック機構を使用してソースコードをハッキングした方がよいソリューションです。
追加された 著者 Raynos,
TL; DR、コンストラクタから呼び出す?
追加された 著者 sll,

4 答え

私は Initalize()メソッドは実装上の問題に似ていると思います。そして、それはおそらくインタフェース内にあってはならないことを意味します。

インスタンスの初期化は、コンストラクタに委ねるのが最善です。

遅延初期化が本当に必要な場合は、boolとlockを使用したソリューションがOKと思われます。

3
追加された
ありがとう。さて、私は通常、私のコンストラクタをきれいに保ち、主にプライベートフィールドを設定し、他のプロパティを計算または検証または初期化する必要がある場合は初期化を遅らせることができます。熱心な初期化と遅れた初期化。
追加された 著者 The Light,
それらのプロパティはすべて一緒に初期化する必要があるため、遅延ロードは機能しません。当初、私はコンストラクタで熱心な初期化を使用していましたが、私はそれが原因で初期化が遅延されると思いました。
追加された 著者 The Light,
後で他のプロパティを計算または検証したり初期化しなければならない場合は、これらのプロパティに遅延ロードを実装しないでください。それはカプセル化の半分のポイントです。
追加された 著者 Michael Paulukonis,

あなたがInitializeメソッドでやっていることで判断すると、あなたが調べる必要があるのは、そのクラスをシングルトンとして登録し、コンテナを永続させることです。ここでこれを行う例を見ることができます:

http://gunnarpeipman.com/2008/04/unity-and-singletons/

1
追加された
私はすでにクラスをシングルトンとして登録しています。私のコードを正しく読みましたか?
追加された 著者 The Light,
私が言ったように、それはすでにシングルトンです!その寿命はUnityによって制御されます。
追加された 著者 The Light,
シングルトンも私の本能だろうし、はい、彼はあなたのAppConfigクラスがパブリックコンストラクタに示されているようにC#のシングルトンパターンに従わないので、あなたのコードを正しく読んだ。
追加された 著者 ChrisBD,
ウィリアム、あなたは正しい。
追加された 著者 David Savage,

さて、Unityに依存して、クラスがシングルトンであることを確認してください。 C#のコードパターンはかなり簡単ですが。 こちらをご覧ください。その後、コンストラクタで初期化コードを呼び出します。

いずれにしても、コードがatmoなので、初期化フラグはvolatileと宣言します。

0
追加された
揮発性が使用されていない理由は、二重検査ロックを使用しているので、必要ではないためです。 Volatileは、あるスレッドが他のスレッドによって書き込まれた最新の値を取得し、そのコードが揮発性なしでその値を保証することを保証します。
追加された 著者 The Light,

getアクセサで初期化されているかどうかを調べる静的クラスのインスタンス変数を使用することをお勧めします。インスタンスプロパティを使用してクラスにアクセスすると、クラスの初期化回数が制御されます。これは、ほとんどデフォルトのC#シングルトンパターンです:

public static class MySingleton 
{
    private static Mutex instanceLock = new Mutex();

    private static MySingleton instance;
    public static MySingleton Instance
    {
        get
        {
            instanceLock.WaitOne();
            if(instance == null)
            {
                instance = new MySingleton();
            }
            instanceLock.ReleaseMutex();
            return instance;
         }
    }

    private MySingleton()
    {
        Initialize();
    }

    private void Initialize()
    {
       //Initialize
    }
}

public class MyOtherClass
{
    private MySingleton singleton = MySingleton.Instance;
}
0
追加された