需要帮助来自Expert F#第13章的这个F#代码

时间:2011-07-05 16:16:38

标签: .net asynchronous f#

缺少IEvent,需要帮助才能完成此代码..我不明白如何制作触发器事件

// ----------------------------
// Listing 13-3.

open System
open System.Threading
open System.ComponentModel
open System.Windows.Forms

/// An IterativeBackgroudWorker follows the BackgroundWorker design pattern
/// but instead of running an arbitrary computation it iterates a function
/// a fixed number of times and reports intermediate and final results.
/// The worker is paramaterized by its internal state type.
///
/// Percentage progress is based on the iteration number. Cancellation checks
/// are made at each iteration. Implemented via an internal BackgroundWorker.
type IterativeBackgroundWorker<'a>(oneStep:('a -> 'a),
                                   initialState:'a,
                                   numIterations:int) =

    let worker =
        new BackgroundWorker(WorkerReportsProgress=true,
                             WorkerSupportsCancellation=true)

    // The constructor captures the synchronization context. This allows us to post
    // messages back to the GUI thread where the BackgroundWorker was created.
    let syncContext = SynchronizationContext.Current
    do if syncContext = null then failwith "no synchronization context found"

    // Create the events that we will later trigger
    let triggerStarted,  started   = IEvent.create()
    let triggerCompleted,completed = IEvent.create()
    let triggerError    ,error     = IEvent.create()
    let triggerCancelled,cancelled = IEvent.create()
    let triggerProgress ,progress  = IEvent.create()

    do worker.DoWork.Add(fun args ->
        syncContext.Post(SendOrPostCallback(fun _ -> triggerStarted(DateTime.Now)),
                         state=null)

        // This recursive function represents the computation loop.
        // It runs at "maximum speed", i.e. is an active rather than
        // a reactive process, and can only be controlled by a
        // cancellation signal.
        let rec iterate state i =
            // At the end of the compuation terminate the recursive loop
            if worker.CancellationPending then
               args.Cancel <- true
            elif i < numIterations then
                // Compute the next result
                let state' = oneStep state

                // Report the percentage compuation and the internal state
                let percent = int ((float (i+1)/float numIterations) * 100.0)
                do worker.ReportProgress(percent, box state);

                // Compute the next result
                iterate state' (i+1)
            else
                args.Result <- box state

        iterate initialState 0)

    do worker.RunWorkerCompleted.Add(fun args ->
        if args.Cancelled       then triggerCancelled()
        elif args.Error <> null then triggerError args.Error
        else triggerCompleted (args.Result :?> 'a))

    do worker.ProgressChanged.Add(fun args ->
        triggerProgress (args.ProgressPercentage,(args.UserState :?> 'a)))

    member x.WorkerCompleted  = completed
    member x.WorkerCancelled  = cancelled
    member x.WorkerError      = error
    member x.ProgressChanged  = progress

    // Delegate the remaining members to the underlying worker
    member x.RunWorkerAsync()    = worker.RunWorkerAsync()
    member x.CancelAsync()       = worker.CancelAsync()

    /// The Started event gets raised when the worker starts. It is
    /// raised on the GUI thread (i.e. in the synchronization context of
    /// the thread where the worker object was created).
    // It has type IEvent<DateTime>
    member x.Started             = started

let fibOneStep (fibPrevPrev:bigint,fibPrev) = (fibPrev, fibPrevPrev+fibPrev)

// ----------------------------

let worker = new IterativeBackgroundWorker<_>( fibOneStep,(1I,1I),100)

worker.WorkerCompleted.Add(fun result ->
      MessageBox.Show(sprintf "Result = %A" result) |> ignore)

worker.ProgressChanged.Add(fun (percentage, state) ->
    printfn "%d%% complete, state = %A" percentage state)

worker.RunWorkerAsync()

// ----------------------------

只需要知道如何制作一个稍后会触发的新事件。但如果有人想要帮助来完成代码,那么这将是一个很大的谢谢:D

2 个答案:

答案 0 :(得分:1)

来自文档:

open System.Collections.Generic

type MyClassWithCLIEvent() =

    let event1 = new Event<_>()

    [<CLIEvent>]
    member this.Event1 = event1.Publish

    member this.TestEvent(arg) =
        event1.Trigger(this, arg)

let classWithEvent = new MyClassWithCLIEvent()
classWithEvent.Event1.Add(fun (sender, arg) -> 
        printfn "Event1 occurred! Object data: %s" arg)

classWithEvent.TestEvent("Hello World!")

答案 1 :(得分:1)

Daniel发布的代码应该给你答案。只需将其链接到Expert F#书的(第一版)中的代码,更改为:

  • IEvent.create()函数已被类型替换,因此您将使用new Event<_>()
  • 返回元组的第一个元素(触发器函数)是对象的Trigger成员
  • 元组的第二个元素(IEvent值)是对象的Publish成员
  • 如果使用CLIEvent属性,则事件将编译为.NET事件(否则它将只是IEvent类型的属性)

我相信这些是用于处理事件的API中唯一的重大变化。还有一个新模块Observable包含与Event相同的功能(例如Event.map),通常最好使用可观察的实现(它更标准,避免潜在的内存泄漏) )。