【问题标题】:Static Access To Multiple Instance Variable静态访问多个实例变量
【发布时间】:2010-04-15 21:11:37
【问题描述】:

我有一个在整个项目中引用的单例实例,它就像一个魅力。它为我省去了将对象实例传递给项目中每个小类的麻烦。但是,现在我需要管理之前设置的多个实例,这意味着单例模式会中断,因为每个实例都需要它自己的单例实例。

还有哪些选项可以保持对单例的静态访问?更具体地说,我们有我们的游戏引擎和几个组件和插件通过静态属性引用引擎。现在我们的服务器需要托管多个游戏实例,每个实例都有自己的引擎,这意味着在服务器端,单例模式会中断。

我试图避免在构造函数中包含引擎的所有类。

编辑:引擎不能保证在唯一的线程上运行。每个引擎都有一个唯一的 ID,可用于识别实例。

【问题讨论】:

  • 只要您使用单例和静态访问,您就必须使用单例的生产版本(紧密耦合)来测试您的类。考虑转向构造函数注入和可选的 DI(依赖注入)容器框架。
  • 构造函数注入是我试图避免的。我们有 50 多个类引用引擎,并且必须更改所有构造函数以获取引擎将是大量工作,当静态变量如此接近我们需要时,感觉有点多余。依赖注入可能是答案。我会调查一下。不过看起来工作量很大。
  • 修改你的单例以使用唯一 ID 作为键在哈希表中查找值。
  • Westgate,所有想要引用单例对象的对象都需要知道该键,从而再次强制所有对象在某些情况下将相同的对象(在本例中为键)作为参数点。
  • 如果重构开销是修改所有构造函数的主要问题,您可能需要查看 Resharper 之类的重构工具。对于每个类,您只需单击 Singleton.Instance 调用,然后单击 Refactor |引入 Field 并替换所有出现的地方。它将引入一个在构造函数中设置的新私有字段。现在只需要将隔离的 Singleton.Instance 调用改为通过构造函数传递的参数,也就是 Refactor |引入参数。现在所有的构造函数调用都会有一个虚拟对象,编译器会在构建时指出它。

标签: c# .net static singleton


【解决方案1】:

这种情况是单例模式崩溃的地方。拥有一个所有代码都可以检索实例的地方很方便,但现在您需要保留多个实例。但是,您是对的,将上下文实例一直传递到对象树的构造函数链是非常烦人的。此问题的一种解决方案是使用控制反转框架,例如Ninject。这是一项设计投资(因为习惯使用 IoC 需要一些时间),但它特别擅长解决这些问题,您想使用依赖注入(将您的 GameEngine 传递给您的各种类),但不要不想写一堆胶水代码只是为了传递引用。

也就是说,如果您只有一个这样的上下文(即只有 GameEngine 是您的单例),那么将它添加到所有构造函数中可能是最简单的。

【讨论】:

    【解决方案2】:

    您可以做的最接近的事情是在您的单例实例变量上使用ThreadStatic 属性。这将在访问方面保持静态语义,但每个线程都有自己的实例。

    public class ThreadStaticSingleton
    {
        [ThreadStatic]
        private static ThreadStaticSingleton instance;
    
        public static ThreadStaticSingleTon Instance
        {
            get 
            {
                if(instance == null) instance = new ThreadStaticSingleton();
    
                return instance;
            }
        }
    }
    

    话虽如此,这可能非常很容易不是你想要的。如果您需要让多个线程通过静态变量访问同一个实例,或者让一个线程通过同一个变量访问不同的个实例,那么这是行不通的。

    换句话说,如果以下两个都为真,这将起作用:

    • 每个游戏引擎都在自己的线程上运行
    • 每个游戏引擎在一个线程上运行

    否则,您可能不得不走工厂模式路线,其中每个引擎将某种识别信息(即使它只是 this)传递给静态函数以获取实例,而不仅仅是使用 @987654324 @属性。

    【讨论】:

    • 非常酷的功能,但不太适合解决这个问题。多个引擎将在同一个线程上运行。拥有一个密钥会导致同样的问题 - 所有使用引擎的类都需要静态访问密钥,然后我们回到基本。
    • @Qua:根据定义,这不再是单例。听起来您的游戏引擎上以前的单例类型的实例变量是需要的。如果不进行一些重构,我认为没有办法解决这个问题。
    • 我们将结构更改为让每个游戏实例在单独的线程上运行。无论如何,我们应该从一开始就拥有。很棒的属性。
    【解决方案3】:

    您可以使用单例对象的字典来实现多实例单例。然后,您的每个游戏实例都需要使用不同的密钥。

    还有其他不使用 Singleton 的方法,但类似的方法可能对您有用(注意:我没有编译或测试它,但它可能会给您一个开始)。

    public class MySingleton
    {
        private static Dictionary<string, MySingleton> myInstances = new Dictionary<string, MySingleton>();
    
        private MySingleton()
        {
            // construct instance
        }
    
        // note: could also implement using an indexer
        // also note: this is not thread-safe, but you could add a lock around it
        public static MySingleton GetInstance(string key)
        {
            if (!myInstances.ContainsKey(key))
            {
                myInstances.Add(key, new MySingleton());
            }
            return myInstances[key];
        }
    
        // other methods
    }
    

    【讨论】:

    • 我的班级如何知道使用什么键?我根本看不出这是如何解决任何问题的。在我需要对我的单例进行静态访问之前 - 现在我需要对一些任意键进行静态访问。
    • 如果您的每个游戏实例都需要访问不同的实例,您必须以某种方式识别每个实例。您如何识别每个独特的游戏实例?你有 GameID 或 GameName 之类的吗?
    • 假设我使用键“kek”创建了一个游戏引擎实例。现在,所有引用这个特定游戏引擎的类的实例都需要知道密钥才能引用游戏实例。除非我以某种方式将密钥传递给它们,否则所有这些实例如何知道密钥?每个游戏/引擎都有一个唯一的 ID。
    • 如果您不希望您的其他游戏对象必须跟踪键,您可能需要开始查看依赖注入框架,以自动将事物的实例注入您的类。见丹布莱恩特的回答。将单实例设计扩展到多实例支持并非易事,您必须做一些事情来区分独特的游戏实例。
    【解决方案4】:

    我不是 100% 确定我已经理解了这个问题,但是您对“实例”一词的使用似乎表明单例的每个对象实例都必须由客户端类的一个对象实例使用。在这种情况下,我会说每个客户端实例只检索一次单例实例并存储在成员变量中。

    但是,听起来您不想向每个客户端类添加代码,在这种情况下,我认为没有办法让单例确定返回哪个实例,因为单例不知道调用它的是什么(缺少线程调用堆栈的一些疯狂技巧)。

    【讨论】:

      猜你喜欢
      • 2014-02-03
      • 2015-08-05
      • 2016-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-19
      • 1970-01-01
      • 2022-08-09
      相关资源
      最近更新 更多