是否有任何情况下,我得到两个不同的单例在同一个应用程序

本文关键字:两个 单例 应用程序 同一个 情况下 任何 是否 | 更新日期: 2023-09-27 18:02:07

如果我考虑单例的基本实现,例如:

private static Foo instance;
private readonly static Object SyncRoot=new Object();
public static Foo Instance {
    get {
        if(instance!=null)
            return instance;
        lock(SyncRoot) {
            if(instance!=null) {
                return instance;
            }
            instance=new Foo();
            return instance;
        }
    }
}

是否有任何情况下,我得到两个不同的单例在同一个应用程序?(动态DLL加载与反射,执行和同步上下文,appdomain类,或任何其他类型的"魔术"?)

是否有任何情况下,我得到两个不同的单例在同一个应用程序

您必须定义"同一应用程序"的含义。如果一个"应用程序"可以跨越多个AppDomain,那么是的——每个AppDomain将有效地拥有一个完全独立的Foo类。同样,如果您有可信代码使用反射将instance字段重置为null,那么您将非常容易地结束两个实例:

var field = typeof(Foo).GetField("instance",
                                 BindingFlags.Static | BindingFlags.NonPublic);
var foo1 = Foo.Instance;
field.SetValue(null, null);
var foo2 = Foo.Instance;

foo1foo2都是非空的,不同的引用。或者正如gdoron的回答所提到的,代码也可以通过反射调用构造函数(可能是私有的)。

在单个AppDomain中,没有任何故意引起问题,您应该没问题。

请注意,我并不推荐这种单例模式的实现。我通常只是使用静态初始化器来简化工作。要了解更多细节,请参阅我关于单例实现的文章。

是的,这是可能的反射,你的代码只适用于属性,反射可以创建没有属性的Foo实例。

ConstructorInfo ctor = typeof(Foo).GetConstructors
        (BindingFlags.Instance | BindingFlags.NonPublic)[0];
Foo foo = (Foo) ctor.Invoke(null);

当然,如果您使用不同的AppDomain,那么每个AppDomain将获得一个实例。我认为如果您在多线程环境中使用锁定机制也可能存在问题。与其在getter中创建实例,不如使用

private static Foo Instance = new Foo();

抗反射单例模式:

public sealed class Singleton
{
    public static Singleton Instance => _lazy.Value;
    private static Lazy<Singleton, Func<int>> _lazy { get; }
    static Singleton()
    {
        var i = 0;
        _lazy = new Lazy<Singleton, Func<int>>(() =>
        {
            i++;
            return new Singleton();
        }, ()=>i);
    }
    private Singleton()
    {
        if (_lazy.Metadata() == 0 || _lazy.IsValueCreated) 
            throw new Exception("Singleton creation exception");
    }
    public void Run()
    {
        Console.WriteLine("Singleton called");
    }
}

然后试试:

    static void Main(string[] args)
    {
        Singleton.Instance.Run();
        ((Singleton) Activator.CreateInstance(typeof(Singleton), true)).Run();
    }