【问题标题】:When using a singleton pattern, should my public class return the private or public instance?使用单例模式时,我的公共类应该返回私有实例还是公共实例?
【发布时间】:2017-01-03 12:01:48
【问题描述】:

我有一个这样定义的单例:

public partial class MoonDataManager
{

    static MoonDataManager _singletonInstance;
    public static MoonDataManager SingletonInstance
    {
        get
        {
            return _singletonInstance;
        }
        private set
        {
            _singletonInstance = value;
        }
    }

我有一个安全地创建实例的函数:

   public static async Task<MoonDataManager> CreateSingletonAsync()
    {
            _singletonInstance = new MoonDataManager();

我应该:

return  _singletonInstance;  (field) 

return SingletonInstance;  (property)

我关心垃圾收集,尤其是在 Xamarin 中的 iOS 或 Android 中。

此外,如果 C# 中有此命名模式,请告诉我是否偏离标准。


更新:

现在我想我真的被线程和异步方法困住了。以下是对象及其目标:

  • MoonDataManager :每个表运行一次RegisterTable&lt;Models.IssuerKey&gt;。这是一个基本运行 (new MobileServiceSQLiteStore).DefineTable&lt;T&gt;()

  • 的通用方法
  • OfflineStore :这是一个MobileServiceSQLiteStore

  • MobileClient:这是一个MobileServiceClient

  • MoonDataManager 依赖关系:MoonDataManager 需要 OfflineStore 和 MobileClient 才能完成初始化。具体来说,它执行MobileServiceClient.SyncContext.InitializeAsync(OfflineStore)

我不确定如何理解这种意大利面条式的依赖关系......或者如何使代码看起来不错,并且是线程安全的。

这是代码的新迭代:

private readonly Lazy<MobileServiceClient> lazyMobileClient = 
        new Lazy<MobileServiceClient>(() => new MobileServiceClient(Constants.ApplicationURL), true); // true for thread safety
    public  MobileServiceClient MobileClient { get { return lazyMobileClient.Value; } }


    private readonly Lazy< MobileServiceSQLiteStore> offlineDB =
        new Lazy<MobileServiceSQLiteStore>(() =>  new MobileServiceSQLiteStore(Constants.OfflineDBName), true ); // true for thread safety
    private MobileServiceSQLiteStore OfflineStore { get { return offlineDB.Value; } }

    private static readonly Lazy<MoonDataManager> lazy =  new Lazy<MoonDataManager>(() => new MoonDataManager(), true); // true for thread safety
    public static MoonDataManager Instance { get { return lazy.Value; } }

    private MoonDataManager()
    {

             MoonDataManager.Instance.RegisterTable<Models.IssuerKey>();

            // Initialize file sync
            // todo: investigate FileSyncTriggerFactory overload. 
            //Was present on Mar 30, 2016 Channel9  https://channel9.msdn.com/events/Build/2016/P408
            MoonDataManager.Instance.MobileClient.InitializeFileSyncContext
                           (new IssuerKeyFileSyncHandler(Instance),   Instance.OfflineStore);

            // NOTE THE ASYNC METHOD HERE (won't compile)
            await MoonDataManager.Instance.MobileClient
                                 .SyncContext.InitializeAsync(MoonDataManager.Instance.OfflineStore,
                                StoreTrackingOptions.NotifyLocalAndServerOperations);

    }

【问题讨论】:

标签: c# xamarin.ios garbage-collection xamarin.android automatic-ref-counting


【解决方案1】:

对于 .NET 4 或更高版本,您可以使用 Lazy&lt;T&gt; 并像这样创建它。

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton(), true); // true for thread safety

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

只有在第一次访问并且它是线程安全的时才会创建它。

【讨论】:

  • 如果 Singleton ctor 包含异步方法,我该如何利用它?
  • 我刚刚更新了问题中的代码,并对此进行了扩展。
【解决方案2】:

定义

static MoonDataManager _singletonInstance;

保证MoonDataManager的实例是一个GC根,直到应用域结束,because it is a static value才会被收集。


我会退回私人单身人士并放弃您拥有的汽车财产。

public partial class MoonDataManager
{
    private static readonly Lazy<MoonDataManager> _manager = 
        new Lazy<MoonDataManager>(() => new MoonDataManager()); 

    public static MoonDataManager SingletonInstance => _manager.Value; 
}

当第一次访问MoonDataManager.Value 时,它会使用传递给Lazy&lt;T&gt; 的构造函数的Func&lt;MoonDataManager&gt; 进行初始化。在后续访问中,返回相同的实例。

【讨论】:

  • 我刚刚更新了问题中的代码,并对此进行了扩展。
【解决方案3】:

Singleton 在第一次被访问时创建自己,以确保只会创建一个实例的方式,即使第二个线程在它仍在实例化时尝试访问它

您的 CreateSingletonAsync() 违反了这一点,并且看起来它允许多线程讨厌

你想要这样的东西:

public static MoonDataManager SingletonInstance
{
    get
    {
        if (_singletonInsatnce != null)
            return _singletonInstance;
        lock (lockobject)
        {
            // check for null again, as new one may have been created while a thread was waiting on the lock
            if (_singletonInsatnce != null)
                return _singletonInstance;
            else
                // create new one here.
        }
    }
    // no setter, because by definition no other class can instantiate the singleton
}

所有这些只是为了确保请求一个对象的两个线程不会最终创建两个对象,或者如果第一个线程的对象仍在创建中,那么第二个线程会获得一个创建一半的对象。

注意:单身人士已经过时了。

注意:如果您可以确定您有时间在对象被访问之前创建它,您可以使用静态成员并在应用程序启动时创建它。

您的问题“我应该返回属性或字段”没有意义 - 您已经从属性获取器返回字段,这是标准做法。您还想在哪里退货?

【讨论】:

  • 如果 Singleton ctor 包含异步方法,我该如何利用它?有什么比单例更好的选择?
  • 我刚刚更新了问题中的代码,并对此进行了扩展。
【解决方案4】:

您应该返回私有实例。您可以在 MSDN 上阅读有关单例模式的更多信息。标准单例实现如下:

public class Singleton
{
    private static Singleton instance;

    private Singleton() {}

    public static Singleton Instance
    {
       get 
       {
          if (instance == null)
          {
             instance = new Singleton();
          }
          return instance;
       }
    }
}

虽然,通常情况下,您没有该属性的设置器。这个模式有already previously been discussed on SO

【讨论】:

  • 真@CodingYoshi。如果多线程是一个问题,应该使用lock。我从 MSDN 附加的链接中也描述了该模式。但问题没有描述这种担忧,所以我省略了这个事实。
  • 这次投票被否决的原因是什么?它很好地回答了这个问题。
猜你喜欢
  • 2022-12-07
  • 2013-04-05
  • 2011-07-21
  • 2014-11-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多