如何实现Singleton Pattern(语法)

aka*_*nom 9 singleton f#

我有一个从外部源刷新的数据缓存,我想限制我的应用程序内部的缓存(readonly)访问.我不希望每次需要访问它时刷新数据源(即,在实例化时去拉取我需要的所有数据,因为有相当多的数据保持最新).

type MySingleton = 

        [<DefaultValue>]
        static val mutable private instance: MySingleton

        static member GetInstance() = 
            instance
Run Code Online (Sandbox Code Playgroud)

我想这是关于实现项目并试图同时学习语言的问题之一.我知道逻辑需要

if instance is null
    synchronize
    if instance is null
        instance = new MySingleton()
Run Code Online (Sandbox Code Playgroud)

但是缺少null会让我陷入困境.我想我可以使用选项类型等,但它让我循环

type MySingleton = 

        [<DefaultValue>]
        static val mutable private instance: MySingleton option

        static member GetInstance() = 
            match instance with
                 | Some(i) -> i
                 | None -> 
                            *MySingleton.instance = new MySingleton()
                            MySingleton.instance*
Run Code Online (Sandbox Code Playgroud)

根据编译器,逻辑是错误的......

       if Helper.notExists MySingleton.instance then
            MySingleton.instance <- Some(new MySingleton())        
       MySingleton.instance 
Run Code Online (Sandbox Code Playgroud)

我应该使用IF语句吗?f#中有这种语法的首选模式吗?

Bri*_*ian 14

.NET 4.0和F#都有Lazy,所以我想你想要

module MySingleton =
    let private x = Lazy.Create(fun() -> 42)
    let GetInstance() = x.Value
Run Code Online (Sandbox Code Playgroud)

(42可能是一个new WhateverType()或任何昂贵的初始化).

http://msdn.microsoft.com/en-us/library/dd997286.aspx

(评论:它是2010年,并且很少需要明确处理同步原语;语言和库正在封装所有常见模式.)

  • @Onorio:单身模式大约是两件事; 让每个人都得到与命名对象相同的引用(这有点像你所说的),并避免初始化代码的不良副作用(效果可能是实际的副作用,或只是'perf'效果,例如很慢,或占用大量内存或其他资源).您希望确保这些效果不会发生多次(您只需要一个对象),并且除非需要,否则它们不会发生(如果没有人使用该对象,则没有效果).懒惰的初始化购买了这些效果保证. (5认同)

Dmi*_*ruk 9

很抱歉要复活一个旧问题,只是想指出有些人可能会尝试Instance在公共属性中公开,在这种情况下,以下代码段可能会有用:

// private constructor, just as any OO singleton
type public MyType private() =
  inherit SomeParent()

  static let mutable instance = lazy(new MyType())

  // Get the instance of the type
  static member Instance with get() = instance.Value
Run Code Online (Sandbox Code Playgroud)

  • 而且,单例模式应该有一个私有构造函数,所以:`type public MyType private()=` (3认同)

Mar*_*ist 7

问题是如何实现Singleton模式,而不是如何实现Lazy-Load模式.单例可以通过多种方式安全地实现,例如:

// Standard approach in F# 2.0: using an implicit constructor.
type Singleton private() =
    static let instance = new Singleton()
    static member Instance = instance

// Abbreviated approach in F# 3.0: using an implicit constructor with auto property.
type Singleton private() =
    static member val Instance = Singleton()

// Alternative example: When you have to use an explicit ctor,
// and also want to check instanciation upon each access of the property.

/// This type is intended for private use within Singleton only.
type private SyncRoot = class end

type Singleton =
    [<DefaultValue>]
    static val mutable private instance: Singleton

    private new() = { }

    static member Instance = 
        lock typeof<SyncRoot> (fun() ->
            if box Singleton.instance = null then
                Singleton.instance <- Singleton())
        Singleton.instance    
Run Code Online (Sandbox Code Playgroud)

编辑
添加了带有私有隐式ctor的简化F#2.0示例,带有显式ctor的示例现在使用单独的私有类型作为同步根.感谢kvb的提示.

编辑2 添加了F#3.0自动属性语法.

  • 1.您可以将默认构造函数设为私有(例如,`类型PersonSingleton private()= ...`),在这种情况下,您可以在类型中使用`static let`.2.不要锁定类型实例; 因为类型是公共的,其他代码也可以锁定它,导致死锁. (2认同)

Tom*_*cek 5

The Lazy type as Brian mentioned is a good place to start with. It allows you to ensure that a computation will be run when the value is needed and it guarantees thread safety, meaning that the computation will run only once (although, in some cases, you may also use PublicationOnly option to specify that multiple threads may start to initialize cache and only the first result will be used).

However, you'll probably also need a mechanism for marking the cache as invalid (e.g. after some specified time) and forcing re-initialization of the cache. Note that this isn't really a Singleton pattern. Anyway, you can still do this in a thread safe way using Lazy, but you'll need to structure the code like this:

module Cache = 
  // returns a lazy value that initializes the cache when 
  // accessed for the first time (safely)
  let private createCacheInitialization() = 
    lazy( // some code to calculate cache 
          cache )
  // current cache represented as lazy value
  let mutable private currentCache = createCacheInitialization()

  // Returns the current cache
  let GetCache() = currentCache.Value
  // Reset - cache will be re-initialized next time it is accessed
  // (this doesn't actually initialize a cache - just creates a lazy value)
  let Reset() = currentCache <- createCacheInitialization()
Run Code Online (Sandbox Code Playgroud)

Of course, you could turn this code into a Cache class that takes only the initialization function and encapsulates the rest of the code into a reusable piece (if you need to cache multiple values, for example).