我有一个非常有趣的情况,我已经发现使用.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模式。
也许我错了,或者我的发现没有意义,但我只想分享并想知道你的观点?
答案 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; } }
}