使用此访问 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
在寻找获取单例实例的方法时 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