.Net中的单例模式是不可能的?

时间:2009-09-22 15:46:37

标签: .net singleton design-patterns

我有一个非常有趣的情况,我已经发现使用.net框架(任何版本)都不可能使用Singleton模式

请看下面的代码

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的帮助下,任何好友都可以打破单身模式。

那么谁有风险?

任何编写某些许可组件的人,其中许可证是作为单例模式实现的。

任何基于服务器的代码都使用了Singleton模式。

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

9 个答案:

答案 0 :(得分:28)

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

答案 1 :(得分:13)

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

答案 2 :(得分:11)

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

我认为没有问题。

答案 3 :(得分:7)

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

通常,代码通常不具有这些权限。

答案 4 :(得分:4)

我认为设计模式不是一种安全形式,而是一种鼓励某种用法的方式。如果有人为解决你的设计限制而遇到那么多麻烦,他们就会得到他们应得的东西。

答案 5 :(得分:2)

如果你想要防止这种边缘情况,只需更改私人ctor:

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

然后你必须添加另一个参数化的私人ctor来实际创建单身...也许带有一个秘密参数,只有initalizer会知道如何通过... 整个课程将是:

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来解决你的单例模式,那那就有问题,不是吗?

答案 6 :(得分:2)

每个客户端许可方案都可以破解。

如果计数器大于1,你可以通过抛出异常来解决这个问题 - 但是再一次,其他代码可以使用Reflection来重置计数器。 调用代码甚至可以在加载之前修改程序集,完全删除许可代码!

没有任何编程语言,混淆器等可以完全保护您免受此攻击 - 如果可能的话,游戏发行商肯定会用它来创建牢不可破的版权保护!

只要您在与代码相同的“安全区域”中获得不受信任的代码;你已经迷路了。

答案 7 :(得分:0)

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

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

但是 - 使用锁定机制,你会受到性能影响,所以你已经权衡了好处......

答案 8 :(得分:0)

检查私有构造函数中的堆栈跟踪,以确保它仅从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; } }
}