使用自动属性实现单例是一个好主意吗?

发布于 2024-08-19 00:05:30 字数 407 浏览 3 评论 0原文

我最近发现了汽车属性并且非常喜欢它们。目前我正尝试在任何可以的地方使用它们。不仅仅是为了能够在任何地方使用它们,更多的是为了看看它们在大多数情况下的工作效果如何。

现在我正在创建一个单例并想:“嘿,让我们在这里也尝试一下自动属性”。

public class MySingleton
{
    public static MySingleton MySingleton { get; private set; }

    private MySingleton() {}

    static MySingleton() { MySingleton = new MySingleton(); }
}

所以我的问题是:“像这样实现单例是个好主意吗?”

我并不是在问单身人士总体上是否是一个好主意。

I recently found out about auto-properties and like them quite a lot. At this moment I am trying to use them everywhere where I can. Not really to just be able to use them everywhere, but more to see how well they work in most situations.

Now I am making a singleton and thought:"Hey, let's try auto-properties here as well".

public class MySingleton
{
    public static MySingleton MySingleton { get; private set; }

    private MySingleton() {}

    static MySingleton() { MySingleton = new MySingleton(); }
}

So my question is: "Is it a good idea to implement a singleton like this?"

I am not asking whether a singleton in general is a good idea.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

忆梦 2024-08-26 00:05:30

我个人不会这样做。我不喜欢使用带有私有 setter 的自动实现的属性,当您真正想要由只读变量支持的只读属性时,您永远不会调用该属性。只需再一行代码就可以更明确地表达您的意思:

public sealed class MySingleton
{
    private static readonly MySingleton mySingleton;
    public static MySingleton MySingleton { get { return mySingleton; } }

    private MySingleton() {}

    static MySingleton() { mySingleton = new MySingleton(); }
}

这样就没有人会试图更改单例类来重新分配属性或变量,因为编译器会阻止它们。他们必须添加一个设置器和/或使变量变为非只读,这是一个更大的更改 - 希望他们会重新考虑这一更改。

换句话说:

  • 是的,它会起作用。
  • 不,我认为这不是一个好主意。

从 C# 6 开始,使用自动实现的只读属性会更容易:

public sealed class MySingleton
{
    public static MySingleton MySingleton { get; } = new MySingleton();    
    private MySingleton() {}         
    static MySingleton() {}
}

I wouldn't personally do that. I don't like using automatically implemented properties with a private setter that you never call where really you want a read-only property backed by a read-only variable. It's only one more line of code to be more explicit about what you mean:

public sealed class MySingleton
{
    private static readonly MySingleton mySingleton;
    public static MySingleton MySingleton { get { return mySingleton; } }

    private MySingleton() {}

    static MySingleton() { mySingleton = new MySingleton(); }
}

This way no-one's even tempted to change the singleton class to reassign either the property or the variable, because the compiler will stop them. They'd have to add a setter and/or make the variable non-readonly, which is a bigger change - one which hopefully they'd reconsider.

In other words:

  • Yes, it will work.
  • No, I don't think it's a good idea.

As of C# 6, this is easier with read-only automatically implemented properties though:

public sealed class MySingleton
{
    public static MySingleton MySingleton { get; } = new MySingleton();    
    private MySingleton() {}         
    static MySingleton() {}
}
灼疼热情 2024-08-26 00:05:30

我会从与乔恩稍微不同的方向来处理这个问题。 无论对象是否是单例,从逻辑上讲,它首先是否最好被建模为属性?

属性应该代表...属性。 (明显队长再次出击!)你知道。颜色。长度。高度。姓名。家长。所有你从逻辑上认为是事物属性的东西。

我无法想象一个对象的属性在逻辑上是单例。也许你已经想出了一个我没有想到的场景;今天早上我还没有吃胡椒博士的减肥药。但我怀疑这是对模型语义的滥用。

你能描述一下什么是单身人士,以及为什么你相信它是某物的属性吗?

话虽如此,我自己也经常使用这种模式;通常是这样的:

class ImmutableStack<T>
{
    private static readonly ImmutableStack<T> emptystack = whatever;
    public static ImmutableStack<T> Empty { get { return emptystack; } }
    ...

“空”在逻辑上是不可变堆栈的属性吗?不。在这里,能够说的令人信服的好处

var stack = ImmutableStack<int>.Empty;

胜过了我对属性逻辑上成为属性的渴望。光说起来

var stack = ImmutableStack<int>.GetEmpty();

就觉得奇怪。

在这种情况下,是拥有一个只读字段和一个常规属性更好,还是一个静态构造函数和一个 autoprop 更好,似乎不是一个有趣的问题,而是首先是否将其设为一个属性。出于“纯粹”的心情,我可能会站在乔恩一边,并将其设为只读字段。但出于懒惰,我也经常使用带有私有 setter 的 autoprop 模式来创建逻辑上不可变的对象。

全面考虑问题怎么样?

I would approach this from a slightly different direction than Jon. Regardless of whether the object is a singleton, is it logically best modeled as a property in the first place?

Properties are supposed to represent... properties. (Captain Obvious strikes again!) You know. Color. Length. Height. Name. Parent. All stuff that you would logically consider to be a property of a thing.

I cannot conceive of a property of an object which is logically a singleton. Maybe you've come up with a scenario that I haven't thought of; I haven't had any Diet Dr. Pepper yet this morning. But I am suspicious that this is an abuse of the model semantics.

Can you describe what the singleton is, and why you believe it to be a property of something?

All that said, I myself use this pattern frequently; usually like this:

class ImmutableStack<T>
{
    private static readonly ImmutableStack<T> emptystack = whatever;
    public static ImmutableStack<T> Empty { get { return emptystack; } }
    ...

Is "Empty" logically a property of an immutable stack? No. Here the compelling benefit of being able to say

var stack = ImmutableStack<int>.Empty;

trumps my desire for properties to logically be properties. Saying

var stack = ImmutableStack<int>.GetEmpty();

just seems weird.

Whether it is better in this case to have a readonly field and a regular property, or a static ctor and an autoprop, seems like less of an interesting question than whether to make it a property in the first place. In a "purist" mood I would likely side with Jon and make it a readonly field. But I also frequently use the pattern of making autoprops with private setters for logically immutable objects, just out of laziness.

How's that for taking all sides of a question?

倾听心声的旋律 2024-08-26 00:05:30

我看不出有什么理由说这是不正确的。毕竟,自动属性只是私有(编译器生成的)支持字段的访问器的语法糖。

I don't see any reason why this wouldn't be correct. After all, auto-properties are just syntactic sugar for accessors for a private (compiler-generated) backing field.

南汐寒笙箫 2024-08-26 00:05:30

当然,我不认为这有什么问题。

Sure, I don't see any problem with that.

却一份温柔 2024-08-26 00:05:30

汽车财产?不,我不会,但会在单例上使用 setter,是的,我会这样做。我认为你想要对它拥有比汽车财产更多的控制权。

...这里非常欢迎建设性的反馈。在这家受人尊敬的公司中,这感觉像是一个勇敢(或愚蠢)的举动......

我的场景是,一个 WPF 应用程序有一个可以加载和保存的当前项目。当前的项目设置在整个应用程序中使用...

  • 在 UI 中的 WPF 绑定中,以便用户可以更改设置,因此有了 INotifyPropertyChanged 界面。
  • 我还使用 Fody.PropertyChanged 但这不会进行静态属性更改,因此 NotifyStaticPropertyChanged< /代码>。
  • INotifyPropertyChanged 与单例属性上的 WPF 绑定配合得很好。
  • Settings 的实例使用 JSON.NET 进行[反]序列化,因此单例上有 [JsonIgnore] 属性。我在将其加载到单例或将其保存到磁盘之前对其进行验证。
  • 记录器是 Serilog。你记录东西,对吧?
  • 单例是带有 public getter 的 public ,因为 WPF 绑定仅适用于公共属性。 internal setter 不会影响它。 Settings 的所有属性对于 WPF 绑定都是公共的。

我在代码中保留了所有“噪音”,因为有些人可能会发现它很有用。

class Settings : INotifyPropertyChanged
{
    private static Settings _currentSettings = new Settings();

    /// <summary> The one and only Current Settings that is the current Project. </summary>
    [JsonIgnore] // so it isn't serialized by JSON.NET
    public static Settings CurrentSettings //  http://csharpindepth.com/Articles/General/Singleton.aspx 
    {
        get { return _currentSettings; }

        // setter is to load new settings into the current settings (using JSON.NET). Hey, it works. Probably not thread-safe.
        internal set 
        {
            Log.Verbose("CurrentSettings was reset. Project Name: {projectName}", value.ProjectName);
            _currentSettings = value;
            _currentSettings.IsChanged = false;
            NotifyStaticPropertyChanged("CurrentSettings");
        }
    }

    // http://10rem.net/blog/2011/11/29/wpf-45-binding-and-change-notification-for-static-properties
    /// <summary> Fires when the Curent CurrentTvCadSettings is loaded with new settings
    ///           Event queue for all listeners interested in StaticPropertyChanged events. </summary>
    public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged = delegate { };
    private static void NotifyStaticPropertyChanged(string propertyName)
    {
        StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
    }

    // various instance properties including ....

    public string ProjectName {get; set;}

    [JsonIgnore]
    public bool IsChanged { get; set; } 
}

用于将单例设置为新加载的项目,settings 只是

Settings settings = new Settings();
// load, save, deserialize, set properties, go nuts
Settings.CurrentSettings = settings;

setter 可能不是线程安全的,但我只将其设置在 UI 线程的一个位置,所以我并不害怕。您可以按照 http://csharpindepth.com/Articles/General 的受人尊敬的建议使其线程安全/Singleton.aspx

我意识到 OP 没有询问 WPF,但我认为它与展示为什么您可能想要设置单例相关。我这样做是因为这是最简单的解决方案。

Auto property? No, I wouldn't but using a setter on a singleton, yes I did. I think you want more control over it than an auto property will give you.

... constructive feedback is more than welcome here, please. This feels like a brave (or foolish) move in this esteemed company ...

My scenario, a WPF app that has a Current Project than can be loaded and saved. The current project settings are used all over the application...

  • in WPF bindings in the UI so the user can change the settings, hence the INotifyPropertyChanged interface.
  • I also use Fody.PropertyChanged but that doesn't do static property changes, hence NotifyStaticPropertyChanged.
  • INotifyPropertyChanged works fine with WPF bindings on the singleton's properties.
  • An instance of the Settings is [de]serialized using JSON.NET, hence the [JsonIgnore] attribute on the singleton. I validate it before loading it into the singleton or saving it to disk.
  • the logger is Serilog. You log stuff, right?
  • the singleton is public with a public getter because WPF bindings only work on public properties. The internal setter doesn't affect it. All the properties of Settings are public for WPF binding.

I left all the "noise" in the code because some may find it useful.

class Settings : INotifyPropertyChanged
{
    private static Settings _currentSettings = new Settings();

    /// <summary> The one and only Current Settings that is the current Project. </summary>
    [JsonIgnore] // so it isn't serialized by JSON.NET
    public static Settings CurrentSettings //  http://csharpindepth.com/Articles/General/Singleton.aspx 
    {
        get { return _currentSettings; }

        // setter is to load new settings into the current settings (using JSON.NET). Hey, it works. Probably not thread-safe.
        internal set 
        {
            Log.Verbose("CurrentSettings was reset. Project Name: {projectName}", value.ProjectName);
            _currentSettings = value;
            _currentSettings.IsChanged = false;
            NotifyStaticPropertyChanged("CurrentSettings");
        }
    }

    // http://10rem.net/blog/2011/11/29/wpf-45-binding-and-change-notification-for-static-properties
    /// <summary> Fires when the Curent CurrentTvCadSettings is loaded with new settings
    ///           Event queue for all listeners interested in StaticPropertyChanged events. </summary>
    public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged = delegate { };
    private static void NotifyStaticPropertyChanged(string propertyName)
    {
        StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
    }

    // various instance properties including ....

    public string ProjectName {get; set;}

    [JsonIgnore]
    public bool IsChanged { get; set; } 
}

usage to set the singleton to a newly loaded project, settings is simply

Settings settings = new Settings();
// load, save, deserialize, set properties, go nuts
Settings.CurrentSettings = settings;

The setter probably isn't thread-safe but I only set it in one place from the UI thread so I'm not scared. You could make it thread safe following the esteemed advice at http://csharpindepth.com/Articles/General/Singleton.aspx

I realise the OP didn't ask about WPF but I think it's relevant to show why you may want to set a singleton. I did it because it is the simplest solution.

‖放下 2024-08-26 00:05:30

使用 lambda 样式(>= C# 6)总结+替代语法,又名计算属性(又名表达式主体成员):

该代码在功能上完全等同于 Jon Skeet 的答案,这里再次使用“实例”。我并不指望这会得到赞扬,但我认为这个更新的总结在一个地方进行了解释是值得的,因为这个 C#6 问题和答案扩展了旧的封闭线程,其中已经讨论了 Singleton 的变体。

您可能会认为带有显式缺失集的自动属性样式更清楚地表达了只读属性的意图,但最终它是基于样式的,并且这两种样式在现代 C# 中都很常见。

public sealed class MySingleton
{
    public static MySingleton Instance => new MySingleton(); // Assure that instantiation is only done once (because of static property) and in threadsafe way, and as this is an alternative style for a readonly-property, there is no setter
    private MySingleton() {} // Assure, that instantiation cannot be done from outside the class        
    static MySingleton() {} // Assure partly lazyness, see below     
}

以下是所有细节和历史参考:

关于懒惰的讨论:http://csharpindepth.com /Articles/General/Beforefieldinit.aspx
简单总结:只要类中没有引入其他静态字段/属性,上述实现就会表现出惰性。
(但这是一个可能依赖于 .NET 实现的领域。)

有关单例和线程安全的不同实现的讨论(较旧的 C# 样式):
帖子

原始 关闭):
C# 的单例模式

Wrap up + alternative syntax with lambda style (>= C# 6) aka computed property (aka expression-bodied member):

The code is functionally fully equivalent to the answer of Jon Skeet, here again with "Instance". I don´t expect kudos for this, but I think this updated wrapup with explanation at one place is it worth, because this C#6 question and answers extend the old closed thread where variations of Singleton have been discussed.

You could argue the automatic-property-style with an explicitly missing set expresses clearer the intent of a read-only property, but in the end it is style based, and both styles are common in modern C#.

public sealed class MySingleton
{
    public static MySingleton Instance => new MySingleton(); // Assure that instantiation is only done once (because of static property) and in threadsafe way, and as this is an alternative style for a readonly-property, there is no setter
    private MySingleton() {} // Assure, that instantiation cannot be done from outside the class        
    static MySingleton() {} // Assure partly lazyness, see below     
}

Here is all detail and historical reference at one place:

Discussion about lazyness: http://csharpindepth.com/Articles/General/Beforefieldinit.aspx
Simplified summary: The above implementation behaves lazy as long as no other static fields/properties are introduced in the class.
(But this is an area which can be .NET implementation-dependent.)

Discussion concerning different implementations of Singleton and thread safety (older C# styles):
http://csharpindepth.com/Articles/General/Singleton.aspx

Original thread (closed):
Singleton Pattern for C#

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文