Pergunta

I have the following class to create a Singelton:

public class Singleton<T> where T : class
{
    #region Constructors

    protected Singleton()
    { }

    #endregion Constructors

    #region Properties

    private static T instance;

    public static T GetInstance()
    {
        if (instance == null)
        { instance = SingletonCreator.Singleton; }

        return instance;
    }

    public static void ClearInstance()
    { instance = null; }

    #endregion Properties

    #region Inner classes

    private class SingletonCreator
    {
        #region Properties

        private static readonly T instance = typeof(T).InvokeMember(typeof(T).Name,
            BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, null, null, CultureInfo.CurrentCulture) as T;

        internal static T Singleton
        { get { return instance; } }

        #endregion Properties
    }

    #endregion Inner classes
}

Then I've created a class 'Person' which is inheriting from this class, thus making it a Singleton.

public class Person : Singleton<Person>
{
    #region Constructors

    protected Person() { }

    #endregion

    #region Properties

    public string Firstname { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    #endregion
}

And finally, I have my unit test:

    [TestMethod]
    public void then_the_new_instance_should_have_default_values_for_the_properties()
    {
        Person.GetInstance().Age = 1;
        Person.GetInstance().Name = "Unit";
        Person.GetInstance().Firstname = "Test";

        Person.ClearInstance();
        Assert.AreEqual(Person.GetInstance().Age, 0, "The age property of the requested instance is incorrect.");
        Assert.AreEqual(Person.GetInstance().Name, "", "The name property of the requested instance is incorrect.");
        Assert.AreEqual(Person.GetInstance().Firstname, "", "The firstname property of the requested instance is incorrect.");
    }

My guess is that the unit test should pass, because I asks an instance when the previous one is cleared.

But the person object has the same values as the object had before the ClearInstance() method.

I assumed that this was a safe implementation but it seems that I'm missing something here.

Someone have a clue?

Foi útil?

Solução

The reason is that it looks like you have two levels in creating your singleton. Your real singleton is actually the SingletonCreator.instance, and that one never gets cleared.

When Singleton<T> is creating an instance, it calls on SingletonCreator.instance to get the actual instance. When you clear, you clear one from Singleton<T>.instance, but next time you create, it pulls existing one from SingletonCreator.instance which already has an instance that is returned to you.

I don't think you need the extra layer of SingletonCreator. Simply move that code to your Singleton<T> and that'll do it.

public class Singleton<T> where T : class
{
    #region Constructors

    protected Singleton()
    { }

    #endregion Constructors

    #region Properties

    private static readonly object instanceLock = new object();
    private static T instance;

    public static T GetInstance()
    {
        if (instance == null)
        {
            lock(instanceLock)
            {
                if (instance == null)
                {
                    instance = typeof(T).InvokeMember(typeof(T).Name,
            BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, null, null, CultureInfo.CurrentCulture) as T; 
                }
            }
        }

        return instance;
    }

    public static void ClearInstance()
    { instance = null; }

    #endregion Properties
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top