我有一个从外部源刷新的数据缓存,我想限制我的应用程序内部的缓存(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年,并且很少需要明确处理同步原语;语言和库正在封装所有常见模式.)
很抱歉要复活一个旧问题,只是想指出有些人可能会尝试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)
问题是如何实现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自动属性语法.
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).