9

我最近发现了自动属性并且非常喜欢它们。此刻,我正试图在我可以使用的任何地方使用它们。不仅仅是能够在任何地方使用它们,而是更多地了解它们在大多数情况下的工作情况。

现在我正在制作一个单身人士并想:“嘿,让我们也在这里尝试一下自动属性”。

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

    private MySingleton() {}

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

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

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

4

6 回答 6

16

我个人不会那样做。我不喜欢将自动实现的属性与您永远不会调用的私有设置器一起使用,因为您真正想要一个由只读变量支持的只读属性。只需多行代码就可以更明确地说明您的意思:

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() {}
}
于 2010-01-22T12:50:26.190 回答
11

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

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

我无法想象一个在逻辑上是单例的对象的属性。也许你想出了一个我没有想到的场景;今天早上我还没有任何饮食 Dr. Pepper。但我怀疑这是对模型语义的滥用。

你能描述一下单例是什么,以及为什么你认为它是某物的属性吗?

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

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();

只是看起来很奇怪。

在这种情况下,拥有一个 readonly 字段和一个常规属性,还是一个静态 ctor 和一个 autoprop 更好,这似乎不是一个有趣的问题,而不是首先是否将其设为属性。在“纯粹主义者”的心情下,我可能会站在乔恩一边,并将其设为只读领域。但我也经常使用为逻辑不可变对象使用私有设置器制作自动道具的模式,只是出于懒惰。

怎么把问题的方方面面都考虑在内?

于 2010-01-22T16:45:29.997 回答
0

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

于 2010-01-22T12:48:16.300 回答
0

当然,我认为这没有任何问题。

于 2010-01-22T12:48:31.307 回答
0

汽车属性?不,我不会,但在单例上使用二传手,是的,我做到了。我认为你想要对它进行更多的控制,而不是汽车属性会给你的。

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

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

  • 在 UI 中的 WPF 绑定中,因此用户可以更改设置,从而更改INotifyPropertyChanged界面。
  • 我也使用Fody.PropertyChanged但这不会更改静态属性,因此NotifyStaticPropertyChanged.
  • INotifyPropertyChanged与单例属性上的 WPF 绑定一起工作正常。
  • 的一个实例Settings使用 JSON.NET [反]序列化,因此[JsonIgnore]是单例上的属性。我在将其加载到单例或将其保存到磁盘之前对其进行验证。
  • 记录器是Serilog。你记录东西,对吧?
  • 单例public带有publicgetter,因为 WPF 绑定仅适用于公共属性。设置器internal不会影响它。的所有属性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://cshapindepth.com/Articles/General/Singleton.aspx上的建议使其线程安全

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

于 2017-07-21T02:06:01.017 回答
0

总结 + lambda 样式 (>= C# 6) aka 计算属性 (aka expression-bodied member) 的替代语法:

该代码在功能上完全等同于 Jon Skeet 的答案,这里再次使用“Instance”。我不希望为此获得荣誉,但我认为这个更新的总结和解释在一个地方是值得的,因为这个 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# 样式)的讨论: http: //csharpindepth.com/Articles/General/Singleton.aspx

原始线程(已关闭): C# 的单例模式

于 2018-09-12T15:08:47.883 回答