如何确保我的类 Initialize 方法仅被调用一次,最好的方法是什么?

发布于 2024-12-12 08:22:10 字数 3583 浏览 0 评论 0原文

我目前正在使用 Unity IoC 容器,这是我的 AppConfig 类。正如您所看到的,Initialize 方法应该只调用一次,我使用了双重锁检查来确保这一点。

如果我的方法不是最好的方法,那么实现这一目标的最佳方法是什么?

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

    /// <summary>
    /// Gets the admin password.
    /// </summary>
    /// <value>The admin password.</value>
    string AdminPassword { get; }

    /// <summary>
    /// Initializes this instance.
    /// </summary>
    void Initialize();
}

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

    /// <summary>
    /// the injectable config manager
    /// </summary>
    private readonly IConfigManager _configManager;

    private readonly ILogger _logger;

    private static readonly object LockObject = new object();

    private static bool _initialized = false;

    #endregion

    #region Constructors

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

    #endregion

    #region Properties

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

    /// <summary>
    /// Gets the admin password.
    /// </summary>
    /// <value>The admin password.</value>
    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<IAppConfig, AppConfig>(new ContainerControlledLifetimeManager(),
                                              new InjectionConstructor(configManager,
                                                                       logger));
var appConfig = container.Resolve<IAppConfig>();
appConfig.Initialize();

I'm currently using Unity IoC Container and here is my AppConfig class. As you can see the Initialize method should be called only once and I have used double lock checking to ensure that.

What would be the best way to implement achieve this if my approach is not the best way?

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

    /// <summary>
    /// Gets the admin password.
    /// </summary>
    /// <value>The admin password.</value>
    string AdminPassword { get; }

    /// <summary>
    /// Initializes this instance.
    /// </summary>
    void Initialize();
}

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

    /// <summary>
    /// the injectable config manager
    /// </summary>
    private readonly IConfigManager _configManager;

    private readonly ILogger _logger;

    private static readonly object LockObject = new object();

    private static bool _initialized = false;

    #endregion

    #region Constructors

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

    #endregion

    #region Properties

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

    /// <summary>
    /// Gets the admin password.
    /// </summary>
    /// <value>The admin password.</value>
    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
}

In the Unity, I'm using the below code:

// IAppConfig
container.RegisterType<IAppConfig, AppConfig>(new ContainerControlledLifetimeManager(),
                                              new InjectionConstructor(configManager,
                                                                       logger));
var appConfig = container.Resolve<IAppConfig>();
appConfig.Initialize();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

相对绾红妆 2024-12-19 08:22:10

我认为 Initalize() 方法更像是一个实现问题。这意味着它可能根本不应该出现在界面中。

初始化实例最好留给构造函数。

如果您确实需要延迟初始化,那么您使用布尔值和锁来解决似乎没问题。

I think an Initalize() method tastes more like an implementation issue. And that means that maybe it shouldn't be in the interface at all.

Initializing an instance is best left to the constructor.

If you really need a delayed Initialize then you solution with a bool and a lock seems OK.

浸婚纱 2024-12-19 08:22:10

根据您在 Initialize 方法中所做的判断,我认为您需要研究的是将该类注册为单例并持久化容器。您可以在此处查看执行此操作的示例:

http://gunnarpeipman.com/2008/ 04/统一和单例/

Judging by what you are doing in the Initialize method, I think what you need to look into is registering that class as a singleton and persisting the container. You can see an example of doing this here:

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

隔岸观火 2024-12-19 08:22:10

好的,所以您依靠 Unity 来确保您的类是单例。虽然 C# 的代码模式非常简单。请参阅此处。然后在构造函数中调用初始化代码。

无论如何,我会声明你的初始化标志是易失性的,因为代码是atmo的。

Okay so you're relying on Unity to ensure that you class is a singleton. Although the code pattern for C# is quite easy. See here. Then call initialisation code in the constructor.

In any case I would declare your initialization flag volatile as the code stands atmo.

终陌 2024-12-19 08:22:10

我更喜欢有一个静态类实例变量来检查它是否已在 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;
}

I prefer to have a static class instance variable that checks to see if it has been initialized in the get accessor. Access the class through the instance property and you will control how many times the class is initialized. This is pretty much the default C# singleton pattern:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文