整个应用程序所需的对象

时间:2009-12-18 13:43:04

标签: c# singleton

我有一个应用程序,当应用程序加载并在整个应用程序中需要时,从应用程序获取一些数据,在应用程序的整个生命周期内保持保存此数据的对象的最佳方法是什么?

此数据需要在应用程序生命周期内创建的大多数其他对象可用。

我前几天得知单身人士不一定是个好主意。特别是在我的应用程序所在的多线程环境中。

我一直在使用Singletons来解决这个问题,但我想知道这是否是解决这个问题的好方法?

编辑:

让我说明一下:我在应用程序启动时要求输入用户名和密码,现在我知道在内存中保留密码是安全和不好的做法,但在整个登录过程中有很多地方我需要这个数据检查各种事情,所以这是我使用Singleton的地方。

6 个答案:

答案 0 :(得分:2)

我建议不要使用定义自己的单例的类,因为这通常意味着你在单元测试中会有痛苦。

如果您使用通用单例,您将拥有相同的功能,但在测试/远离单例模式(例如,多用户)时可以获得额外的好处。

请注意,使用委托初始化单例。这里的基本原理是委托只会被调用一次,即使两个线程以某种方式同时注册单例......

使用界面可以让您在编写单元测试时更轻松,因为您可以模拟您感兴趣的单例部分进行测试(或者超快速 - 在演示到CEO补丁/调试会话之前2分钟)。 / p>

存储登录/传递元组可能有点过分,但这种模式比我想算的更多次保存我的培根。

public static class Singleton<T>
{
    private static T instance;
    private static readonly object sync = new object();
    static bool registered = false;

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

    public static void Register(Func<T> constructor)
    {
        lock (sync)
        {
            if (!registered)
            {
                instance = constructor();
                registered = true;
            }
        }
    }
}

class Demo
{
    class Data
    {
        public string Pass { get; set; }
        public string Login { get; set; }
    }

    void SimpleUsage()
    {
        string login = "SEKRIT";
        string pass = "PASSWORD";

        // setup 
        Singleton<Data>.Register(() => new Data { Login = login, Pass = pass });

        // 
        var ltCommander = Singleton<Data>.Instance;
    }

    /// <summary>
    /// Using an interface will make the singleton mockable for tests! 
    /// That's invaluable when you'll want to fix something FAST without running the whole app!
    /// </summary>
    interface IData
    {
        string Login { get; }
        string Password { get; }
    }

    class UnitTestFriendlyData : IData
    {
        public UnitTestFriendlyData(string login, string password)
        {
            Login = login;
            Password = password;
        }


        public string Login { get; private set; }
        public string Password { get; private set; }
    }

    void SmarterUsage()
    {
        // same setup, but through the interface. 
        Singleton<IData>.Register(() => new UnitTestFriendlyData("login", "pass"));

        // and same for the retrieval
        var data = Singleton<IData>.Instance;

    }


    void UnitTestSetupWithMoq()
    {
        // Register a mock.
        var mock = new Mock<IData>();
        mock.SetupProperty(x => x.Login, "Login");
        mock.SetupProperty(x => x.Pass, "Pass");
        Singleton<IData>.Register(() => mock.Object);

        // and same for the retrieval
        var data = Singleton<IData>.Instance;

    }

}

答案 1 :(得分:1)

请参阅此处了解一些解释

Implementing Singleton in C# ,查看 Multithreaded Singleton

同样Ist way of implementing Singleton Pattern in C#:查看在C#中实现Singleton Pattern的第三种方式:简单多线程单例模式在C#中实现Singleton模式的第四种方式:多线程单例模式

答案 2 :(得分:1)

在这种情况下,单身是正确的选择。你只是不想在那里开始讨论许多不相关的东西 - 你希望这个课程保持凝聚力,而不仅仅是一个“一揽子财产”。就多线程而言,你可以在你的单例类上放置适当的控件,没问题。但是,您使用哪种类型的锁和保护是特定于您的实现(尽管在您的问题中没有足够的细节来回答这个问题。)

答案 3 :(得分:1)

我对此的第一反应是,如果您拥有应用程序中大多数其他类型所需的数据,您可能希望更好地封装它。这听起来违反了单一责任原则。但是,如果不了解您的情景,很难说出可能的补救措施。

答案 4 :(得分:1)

从您描述您的情况的方式来看,听起来您只是想在启动时保存一次字符串,然后在其他任何地方都只是只读。如果是这种情况,你可以真正做到这样的事情:

internal static class LoginInfo
{
    internal static string Username;
    internal static string Password;
}

然后,您可以在应用中的任何位置说:

var usr = LoginInfo.Username;
var pwd = LoginInfo.Password;

现在我确定每个人都会评论这是一个糟糕的设计实践,但我已经准备好接受这个:)

现在,如果您计划一直更改值,并且如果这不仅仅是一个字符串而是一些更复杂的对象,那么线程安全肯定会成为一个问题。你总是可以在一个属性上创建一个线程安全的getter。

答案 5 :(得分:0)

人们说单身人士不一定是个好主意的原因是因为它鼓励像你这样的场景。需要一个静态对象是不好的部分 - 一个Singleton只被看作是一个推动者。

那就是说,我无法评论你的应用程序中是否有必要,因为你还没有详细说明。但是如果你真的需要在一个对象中保存应用程序生命周期中的静态数据,那么请直接使用单例。 You can make them thread-safe as well.