【问题标题】:Singleton Pattern in .Net is not possible?.Net 中的单例模式是不可能的?
【发布时间】:2009-09-22 15:46:37
【问题描述】:

我有一个非常有趣的情况,我发现 .net 框架(任何版本)都无法实现单例模式

看看下面这段代码

namespace SingletonPattern
{
    class Singleton
    {
    private static readonly Singleton instance = new Singleton();
    private static int mcount = 0;

    private Singleton() {

        mcount += 1;
        Console.WriteLine("Creating {0} instances of Singleton Class", mcount.ToString());
    }

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

class program
{
    static void Main()
    {
        for (int i = 0; i < 1000; i++)
        {
            System.Activator.CreateInstance(Type.GetType("SingletonPattern.Singleton"), true);
        }

        Console.ReadLine();


        }
    }
}

在 System.activator 的帮助下,任何伙伴都可以打破单例模式。

那么谁有风险?

任何编写许可组件的人,其中许可被实现为单例模式。

任何使用单例模式的基于服务器的代码。

也许我错了,或者我的发现没有意义,但我只是想分享并想知道你的观点?

【问题讨论】:

  • 如果您使用单例来强制执行任何种安全性(例如防止违反许可证),那么您的代码(而不是单例)从根本上被破坏了。单例是一种设计模式,旨在使您的代码更易于维护(是否实现这是另一回事)。它从未打算强制执行或提供任何类型的安全性。通常,使用反射可以访问 all 私有成员,而不仅仅是构造函数。这并不意味着 .NET 框架已损坏。它提供了一些易于使用的功能,但它们从不假装“安全”
  • 既然他在征求意见和意见,你为什么要投票给他。只是教育他(正如许多回答者所做的那样)。
  • 如果功能导致一些不好的情况,它的用途是什么。但感谢您的投票和回复
  • 问题是Activator.CreateInstance可以用在很多好的情况下,尤其是处理Reflection和动态加载/执行/创建类型。不幸的是,即使不是所有的好东西,大多数也可以重新用于邪恶的事情(至少在编程中)。

标签: .net singleton design-patterns


【解决方案1】:

仅仅因为可以故意以这种方式规避模式并不意味着该模式本身是“不可能”。

【讨论】:

  • 其实这家伙说的有道理。如果您有充分的理由创建一个单例(无论出于何种原因,您只希望您的类中存在 1 个),那么您可以随时使用 Activator.CreateInstance 来创建一个副本(覆盖您的单例
【解决方案2】:

单例的这个实现确实是错误的,但这并不意味着有人不能创建更好的单例实现。

【讨论】:

【解决方案3】:

同理,所有私有方法都不是私有的,因为您可以使用反射访问它们。

我认为这没有问题。

【讨论】:

    【解决方案4】:

    要使用 System.Activator.CreateInstance,您需要高权限。当然,如果您因为系统信任您而被允许忽略访问修饰符,那么您可以破坏依赖于消费者尊重访问修饰符的代码。

    通常,代码通常没有这些权限。

    【讨论】:

    • 没错。如果你授予代码做某事的权限,那么你就不能抱怨它有权做这件事。
    【解决方案5】:

    我不认为设计模式是一种安全形式,而是一种鼓励某种使用的方式。如果有人为了解决您的设计限制而费尽心思,他们会得到应得的。

    【讨论】:

      【解决方案6】:

      每个客户端许可方案都可能被破坏。

      如果计数器大于 1,您可以通过抛出异常来解决此问题 - 但同样,其他代码可以使用反射来重置计数器。 调用代码甚至可以在加载之前修改您的程序集,完全删除许可代码!

      没有任何编程语言、混淆器等可以完全保护您免受这种情况的影响 - 如果可能的话,游戏发行商肯定会使用它来创建牢不可破的复制保护!

      一旦您在与您的代码相同的“安全区域”中获得不受信任的代码;你已经输了。

      【讨论】:

        【解决方案7】:

        如果您想防止这种极端情况,只需更改私有 ctor:

        private Singleton() { throw new ApplicationException{
             "Don't call System.Activator.CreateInstance on this class"); }
        

        然后你必须添加另一个参数化的私有 ctor 来实际创建单例......也许有一个只有初始化器知道如何传递的秘密参数...... 整个班级将是:

        class Singleton
        {    
           private static readonly Singleton inst = new Singleton("MySecretWord");    
           private static int mcount = 0;   
           private Singleton(string secret)
           { if (secret != "MySecretWord")  
               throw new ApplicationException{
                   "Don't call Private constructor on this class"); 
           }
           private Singleton() { throw new ApplicationException{
             "Don't call System.Activator.CreateInstance on this class"); }
          public static Singleton Instance { get  {  return inst ;  } }
        }
        

        但是为什么要这么麻烦呢?如果有人想麻烦地使用 CreateInstance 来打破你的单例模式,那就有问题了,不是吗?

        【讨论】:

        • Erm,对不起,如果我很愚蠢,但这不会起作用,因为需要调用私有构造函数。
        • @darren,在您发表评论时进行编辑,以准确添加这一点!
        • 即使那样他们也可以使用反射器之类的工具来找到您的秘密单词..
        • @markt,难道不是必须在 CLR 框架中重新编码 CreateInstance 才能调用参数化的私有 ctor 吗?
        • 也许你是对的——我没有意识到你只能用 CreateInstance 调用无参数的私有构造函数......很有趣。
        【解决方案8】:

        如果您担心其他代码试图破坏这种模式,您可以在您的实现中使用锁、信号量或互斥锁等东西来管理实例的数量。

        ...这可能最终使它变得更加困难,因为仍然可以再次使用反射来规避这个...

        但是 - 使用锁定机制,您会受到性能影响,因此您需要权衡好处..

        【讨论】:

          【解决方案9】:

          检查您的私有构造函数中的堆栈跟踪,以确保它仅从 Singleton 类调用,而不是从其他任何地方调用。

          using System;
          using System.Diagnostics;
          
          class Program
          {
              public static void Main(string[] args)
              {
                  //This should work and not throw any exception
                  Singleton singleton = Singleton.Instance;
                  Debug.Assert(singleton != null);
          
                  //This should throw an exception
                  Singleton s = (Singleton)Activator.CreateInstance(typeof(Singleton), true);
              }
          }
          
          public class Singleton
          {
              private static Singleton uniqueInstance = new Singleton();
          
              private Singleton() 
              {
                  StackTrace trace = new StackTrace();
                  StackFrame frame = trace.GetFrame(1);
          
                  //Check that the private constructor is only getting invoked by the static initializer, otherwise throw exception
                  if (String.Compare(trace.GetFrame(1).GetMethod().Name, ".cctor") != 0)
                      throw new InvalidOperationException(
                          "Don't call Private constructor on this class. Abide by Singleton semantics.");
              }
          
              public static Singleton Instance { get { return uniqueInstance; } }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-08
            • 2012-03-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多