如何实现Singleton Pattern(语法)

时间:2010-04-22 14:13:06

标签: f# singleton

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

type MySingleton = 

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

        static member GetInstance() = 
            instance

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

if instance is null
    synchronize
    if instance is null
        instance = new MySingleton()

但是缺少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*

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

       if Helper.notExists MySingleton.instance then
            MySingleton.instance <- Some(new MySingleton())        
       MySingleton.instance 

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

4 个答案:

答案 0 :(得分:14)

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

module MySingleton =
    let private x = Lazy.Create(fun() -> 42)
    let GetInstance() = x.Value

(其中42可能是new WhateverType()或任何昂贵的初始化)。

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

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

答案 1 :(得分:9)

很抱歉重新提出一个旧问题,只是想指出有些人可能会尝试在公共属性中公开{​​{1}},在这种情况下,以下代码可能会有用:

Instance

答案 2 :(得分: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    

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

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

答案 3 :(得分:4)

Brian提到的Lazy类型是一个很好的开始。它允许您确保在需要值时运行计算并确保线程安全,这意味着计算只运行一次(尽管在某些情况下,您也可以使用PublicationOnly option来指定多个线程可能会开始初始化缓存,只会使用第一个结果。)

但是,您可能还需要一种机制来将缓存标记为无效(例如,在某个指定时间之后)并强制重新初始化缓存。请注意,这实际上不是 Singleton模式。无论如何,您仍然可以使用Lazy以线程安全的方式执行此操作,但您需要构建如下代码:

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

当然,您可以将此代码转换为仅占用初始化函数的Cache类,并将其余代码封装到可重用的部分中(例如,如果您需要缓存多个值)。 / p>