使用此访问 c# 中的单例 class;

Accessing a singleton class in c# with this;

在寻找获取单例实例的方法时 class 我发现了许多不同的方法(有些简单,有些复杂)但是当四处乱逛时我找到了一种我没有找到的方法其他任何地方。

所以我基本上做的是:

public class Foo
    { 
       public static Foo Invoker;
       public Foo() 
       {
          Invoker = this;

       }
       public void Method1()
       { 
         //.....
       }
    }

然后从另一个class

public class Foo2
    { 

       public Foo2() 
       {
          //.....

       }
       public void Main()
       {
          var foo = Foo.Invoker;
          //or
          Foo.Invoker.Method1();
       }
    }

我的应用程序是单线程的,所以我不关心线程安全(我应该吗?),那么这种方法是否会导致我遗漏任何其他问题?

首先,你的 'singleton' 模式很容易被打破。假设我将在您的应用程序中创建 Foo 的两个实例(为清楚起见,将 class Foo1 的名称更改为 Bar):

var firstFoo = new Foo();

var bar = new Bar();

// Will access firstFoo when it calls Foo.Invoker
bar.Main();

var secondFoo = new Foo();

// Will access secondFoo when it calls Foo.Invoker. Huh?
bar.Main();

另一个问题:假设我使用 Bar,但没有初始化任何 Foo 个实例:

var bar = new Bar();

// Will throw a NullReferenceException, because Foo.Invoker is not yet initialized.
bar.Main();

作为一个粗略的经验法则,你不应该从实例中设置静态字段,因为它会导致这些情况。 其次,Bar 一开始可能不需要知道 Foo 是单例。您可以简单地在 Bar 的构造函数中注入 Foo

public class Bar
{ 
    private Foo foo;
    public Bar(Foo foo) => this.foo = foo ?? throw new ArgumentNullException(nameof(foo));

    public void Main()
    {
        // Now we know it is not null and, for Bar, it does not matter whether it's a singleton or not.
        foo.Method1();
    }
}

这样您可以在您的应用程序中更轻松地管理 Foo 个实例:

var firstFoo = new Foo();
var bar = new Bar(firstFoo);

// Does not make a difference now.
var secondFoo = new Foo();

这样,您还可以利用 NInject 或 Microsoft.Extensions.DependencyInjection 等依赖注入容器来为您管理单例。

如果您真的想创建单线程单例模式,我会阅读 Jon Skeet 的 blog post 关于单例的文章(读得好!)。

创建单例的最简单方法就是这种方法。通过这种方式,您可以在静态 属性 上创建 Foo 的单个实例,该实例永远无法更改。但是阅读博客 post 以获得更高级的模式:

public class Foo
{
    public static Foo Invoker { get; } = new Foo();

    // Private constructor makes sure the only instance of Foo is created here.
    private Foo()
    {
    }
}

编辑

如果您想确保应用程序中对 Foo 的所有引用都指向最后创建的 Foo 实例,您可以尝试这样的操作:

interface IFoo
{
    void Method1();
}

class Foo : IFoo
{
    private static int index = 1;
    private int id;
    private static NestedFoo invoker = new NestedFoo();
    public static IFoo Invoker
    {
        get
        {
            if (invoker.Instance == null)
            {
                Create();
            }

            return invoker;
        }
    }
    private Foo(int id) => this.id = id;
    public static IFoo Create()
    {
        var foo = new Foo(index++);
        invoker.Instance = foo;
        return invoker;
    }

    public void Method1()
    {
        Console.WriteLine(this.id);
    }

    private class NestedFoo : IFoo
    {
        public Foo Instance { get; set; }
        public void Method1() => Instance.Method1();
    }
}

现在您将始终引用同一个 foo 实例:

var foo = Foo.Create();
foo.Method1(); // 1
var foo2 = Foo.Create();
foo.Method1(); // 2
foo2.Method1(); // 2