F# F中的中介作用#
我有一个问题,我想基于f#中的singelton创建某种中介。主要原因是我需要在应用程序的一个部分注册一些操作,然后在另一个类似于c#的部分执行它,如下所示:F# F中的中介作用#,f#,F#,我有一个问题,我想基于f#中的singelton创建某种中介。主要原因是我需要在应用程序的一个部分注册一些操作,然后在另一个类似于c#的部分执行它,如下所示: public class Mediator { private static readonly object syncRoot = new object(); private static Mediator instance; public static Mediator Instance {
public class Mediator
{
private static readonly object syncRoot = new object();
private static Mediator instance;
public static Mediator Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Mediator();
}
}
}
return instance;
}
}
public event Action<string> InputLogUpdate;
public void InvokeInputLogUpdate(string msg)
{
InputLogUpdate?.Invoke(msg);
}
}
Mediator.Instance.InvokeInputLogUpdate(msg);
Mediator.Instance.InputLogUpdate += msg => this.DebugSection += $"{msg} \n";
注册表看起来像:
public class Mediator
{
private static readonly object syncRoot = new object();
private static Mediator instance;
public static Mediator Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Mediator();
}
}
}
return instance;
}
}
public event Action<string> InputLogUpdate;
public void InvokeInputLogUpdate(string msg)
{
InputLogUpdate?.Invoke(msg);
}
}
Mediator.Instance.InvokeInputLogUpdate(msg);
Mediator.Instance.InputLogUpdate += msg => this.DebugSection += $"{msg} \n";
但现在运气不好,我所得到的只是:
vnamespace Operation.Mediator
module Mediator =
open System
type UdpMediator private () =
static let instance = UdpMediator()
let mutable UpdateDebigLog : Action<string> = null
static member Instance = instance
member this.InvokeUpdateLog (msg:string) = UpdateDebigLog.Invoke(msg)
vnamespace操作.Mediator
模块中介=
开放系统
类型UdpMediator private()=
静态let实例=UdpMediator()
让可变UpdateBigLog:Action=null
静态成员实例=实例
成员this.InvokeUpdateLog(msg:string)=updatedBiglog.Invoke(msg)
而且它的效果也不是很好,任何人都知道如何做到这一点。尝试在F#中使用C#、OOP风格的设计模式通常会导致与用惯用F#表达事物的自然方式相比,事情过于复杂。您所说的您需要的是一种方法,让代码的一部分注册一个操作,如果已注册,则让代码的另一部分执行已注册的操作。因为您使用的是事件,所以我假设您希望能够注册多个操作并触发所有操作。这里有一些代码可以做到这一点:
module ActionRegistry
let mutable private actions : (string -> unit) list = []
let register f =
actions <- f :: actions
let invokeRegisteredActions() =
actions
|> List.iter (fun action -> action())
模块操作注册表
让可变私有操作:(字符串->单位)列表=[]
让我们注册f=
actions List.iter(趣味动作->动作())
现在,这不是比单例中介类更容易理解吗?OOP风格的设计模式总是使事情过于复杂
另外,你没有提到注销操作,所以我让设计简单,没有包含注销功能。如果您确实需要注销操作,我会做一些事情,比如保留一个(int,action)对的列表,使用一个简单的递增计数器确保对的int部分不断增加。然后,register函数将返回一个int,unregister函数将获取一个int并过滤掉第一个位置的int对。首先,夹在两个
null
检查之间的锁的模式太过时了,我甚至不记得上次看到它是什么时候了。现代的.NET为这种按需初始化提供了Lazy
。幸运的是,F#实际上有特殊的语法:
type UpdMediator private () =
static member val private _instance = lazy UpdMediator()
static member Instance = UpdMediator._instance.Value
秒,C#中的事件动作
不等同于F#中的可变动作
。C中的事件
声明实际上创建了两个访问器方法-添加
和删除
,就像获取
和设置
属性一样
然而,F#实际上有一个更好的机制-事件
。将该类型的实例设为私有,然后通过其.Publish
属性将其公开,该属性的类型为IEvent
。使用者可以使用该IEvent
值添加和删除处理程序,或订阅事件可观察样式(因为IEvent
继承自IObservable
):
类型UpdMediator private()=
...
成员val private _updDebugLog=Event()
成员this.UpdDebugLog=this.\u UpdDebugLog.Publish
成员this.InvokeUpdDebugLog()=this.\u updDebugLog.Trigger“foo”
//用法:
UpdMediator.Instance.UpdDebugLog.AddHandler(趣味发件人str->printfn“%s”str)
//或可观察的风格:
UpdMediator.Instance.UpdDebugLog.Subscribe(printfn“%s”)
(请注意,.Subscribe
返回一个IDisposable
,稍后可用于取消订阅)
第三,试图将C#实践逐字翻译成F#实践通常不会有好结果。F#通常比C#有更好的功能,因此逐字翻译通常会有点尴尬,正如您的案例所示
最后,请注意,“工作不太好”不是问题的好描述。如果您希望从社区获得指导或建议,请花时间描述问题的具体情况、您预期会发生什么、您如何尝试实现它,等等。有关一些提示,请参阅。请注意,这将按与注册顺序相反的顺序触发事件,例如后进先出顺序。如果需要FIFO顺序,则将f::actions
更改为List.append actions[f]
。这是一个O(N)运算,而不是O(1),所以你的整体运算是O(N^2)。但是我怀疑你会记录更多的动作,所以对于低N,O(N)和O(N^2)之间的差异真的不重要;警察用了锁。如果需要线程安全,System.Collections.Concurrent.ConcurrentQueue或System.Collections.Concurrent.ConcurrentStack将比普通列表更好。或者在模块内使用单个MailboxProcessor
来管理状态并按顺序调用操作。