Hoe u ervoor kunt zorgen dat de methode Initialiseren van mijn klasse slechts één keer wordt aangeroepen, wat is dan de beste benadering?

Ik gebruik momenteel Unity IoC Container en hier is mijn AppConfig-klasse. Zoals je ziet, zou de Initialize-methode maar één keer moeten worden aangeroepen en ik heb dubbele lock-controle gebruikt om dat te garanderen.

Wat zou de beste manier zijn om dit te bereiken bereiken als mijn aanpak niet de beste manier is?

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
}

In de Eenheid gebruik ik de onderstaande code:

// IAppConfig
container.RegisterType(new ContainerControlledLifetimeManager(),
                                              new InjectionConstructor(configManager,
                                                                       logger));
var appConfig = container.Resolve();
appConfig.Initialize();
0
Ik zal het een keer bellen, maar ik wil er zeker van zijn dat het een keer wordt gebeld en niet twee keer per ongeluk!
toegevoegd de auteur The Light, de bron
Het probleem met het aanroepen van constructeur is dat het minder testbaar is.
toegevoegd de auteur The Light, de bron
Waarom noem je het niet één keer. Wat is er met deze super over de top defensieve programmering.
toegevoegd de auteur Raynos, de bron
statische analyse van de broncode is een betere oplossing dan het hacken van je broncode in stukken met sluitmechanismen.
toegevoegd de auteur Raynos, de bron
TL; DR, noem het van de constructeur?
toegevoegd de auteur sll, de bron

4 antwoord

Ik denk dat een Initalize() -methode meer op een implementatieprobleem lijkt. En dat betekent dat het misschien helemaal niet in de interface zou moeten staan.

Het initialiseren van een instantie kan het best aan de constructor worden overgelaten.

Als je echt een uitgestelde Initialize nodig hebt, dan lijkt je oplossing met een bool en een slot OK.

3
toegevoegd
Bedankt. Nou, ik zou mijn constructor meestal schoonhouden en het vooral gebruiken om privévelden in te stellen en vertraagde initialisatie te gebruiken als andere eigenschappen moeten worden berekend, gevalideerd of geïnitialiseerd. Eager Initialisatie versus vertraagde initialisatie.
toegevoegd de auteur The Light, de bron
Omdat al die eigenschappen samen moeten worden geïnitialiseerd, zou lui laden niet werken. Aanvankelijk gebruikte ik enthousiaste initialisatie in de constructor, maar ik dacht dat ik de initialisatie om de genoemde reden zou uitstellen.
toegevoegd de auteur The Light, de bron
Als andere eigenschappen moeten worden berekend of gevalideerd of geïnitialiseerd later , waarom implementeer je dan geen lui-laden op die eigenschappen, zodat de klas voor die inspanning zorgt, niet voor de belcode? Dat is het halve punt van inkapseling.
toegevoegd de auteur Michael Paulukonis, de bron

Afgaand op wat je aan het doen bent in de Initialize-methode, denk ik dat het belangrijk is om die klasse te registreren als een singleton en de container aan te houden. U kunt hier een voorbeeld van zien doen:

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

1
toegevoegd
Ik heb de klasse al geregistreerd als singleton; heb je mijn code goed gelezen?
toegevoegd de auteur The Light, de bron
Zoals ik al zei, het is al singleton! zijn levensduur wordt gecontroleerd door Unity.
toegevoegd de auteur The Light, de bron
Een singleton zou ook mijn instinct zijn en ja hij heeft je code correct gelezen omdat je AppConfig-klasse het singleton-patroon voor C# niet volgt zoals getoond door de openbare constructor.
toegevoegd de auteur ChrisBD, de bron
William, je hebt gelijk.
toegevoegd de auteur David Savage, de bron

Oké, dus je vertrouwt op Unity om ervoor te zorgen dat je les een singleton is. Hoewel het codepatroon voor C# vrij eenvoudig is. Zie hier . Roep vervolgens de initialisatiecode op in de constructor.

In elk geval zou ik uw initialiseringsvlag volatiel verklaren als de code atmo is.

0
toegevoegd
De reden waarom vluchtig niet is gebruikt, is omdat het niet nodig is, omdat ik dubbel controlevergrendeling gebruik. Volatile zorgt ervoor dat één thread de meest actuele waarde terughaalt die is geschreven door een andere thread en de code zorgt ervoor dat deze zonder vluchtig is.
toegevoegd de auteur The Light, de bron

Ik geef de voorkeur aan een statische klasseninstantie-variabele die controleert of deze is geïnitialiseerd in de get accessor. Open de klasse via de instantie-eigenschap en u bepaalt hoe vaak de klasse wordt geïnitialiseerd. Dit is vrijwel het standaard C# singleton-patroon:

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
toegevoegd