F# interactive is a powerful development tool as it allows to run either WinForm or Wpf window and invoke arbitrary code in there.
This gives a way for a 'try-before-you code' approach.
Very often I wish to 'break the boundaries' explicitly and
invoke private/protected methods
access/change private fields and properties
Is there a workaround to achieve this?
FSI doesn't provide any particular support for this, but you can use Reflection to do the things you want.
open System.Reflection
let field = typeof<MyType>.GetField("fieldName", BindingFlags.NonPublic ||| BindingFlags.Instance)
field.SetValue(myInstance, newVal)
You can go further and define methods or operators to make this even easier. For instance you can set up F#'s dynamic assignment operator to assign to private fields:
let (?<-) o s v =
let field = (o.GetType()).GetField(s, BindingFlags.NonPublic ||| BindingFlags.Instance)
field.SetValue(o,v)
myInstance?fieldName <- newVal (* Note: no quotes around fieldName here *)
Here's some crude code to resolve public or private fields, properties, or methods. Note that there are plenty of ways in which this will fail (in particular, trying to use it on overloaded methods will not work).
open System
open System.Reflection
open Microsoft.FSharp.Reflection
type DynamicHelper =
static member MkMethod<'t,'u> (mi:MethodInfo) o : 't -> 'u=
let typ = typeof<'t>
fun t ->
let args =
if (typ = typeof<unit>) then [||]
else
if not (FSharpType.IsTuple typ) then [| box t |]
else
FSharpValue.GetTupleFields t
mi.Invoke(o, args) :?> 'u
let (?) (o:'a) s : 'b =
let ty = o.GetType()
let field = ty.GetField(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
if field <> null then field.GetValue(o) :?> 'b
else
let prop = ty.GetProperty(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
if prop <> null then prop.GetValue(o, null) :?> 'b
else
let meth = ty.GetMethod(s, BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic)
let d,r = FSharpType.GetFunctionElements(typeof<'b>)
typeof<DynamicHelper>.GetMethod("MkMethod").MakeGenericMethod([|d;r|]).Invoke(null, [| box meth; box o |]) :?> 'b
With this you can dynamically invoke methods and properties as such:
let (t:System.Type) = "test"?GetType()?BaseType
Related
I'm using union types similar to enums on my dapper objects:
type Confidence =
| Low
| Medium
| High
type Goal = {
Confidence: Confidence
...
}
I've created a custom type handler in order to make it work:
type UnionHandler<'T>() =
inherit SqlMapper.TypeHandler<'T>()
override __.SetValue(param, value) =
param.Value <- value.ToString()
()
override x.Parse(value: obj) =
Union.parse <| string value
let registerTypeHandlers() =
SqlMapper.AddTypeHandler (UnionHandler<Confidence>())
This works fine, but it would be even nicer if I didn't have to register a new one for each new union type.
Is it possible to make the type handler generic in such a way that it can handle all union types with only one registration?
This can be done with Reflection:
let internal addUnionTypeHandlers() =
let assembly = Assembly.GetExecutingAssembly()
let unionHandlerType =
assembly.GetTypes()
|> Seq.filter(fun t -> t.Name.Contains("UnionHandler") && t.IsGenericTypeDefinition)
|> Seq.head
assembly.GetTypes()
|> Seq.filter(fun t -> not t.IsGenericType && FSharpType.IsUnion(t, BindingFlags.Default))
|> Seq.iter(fun t ->
let ctor = unionHandlerType
.MakeGenericType(t)
.GetConstructor(Array.empty)
.Invoke(Array.empty)
(typeof<SqlMapper>.GetMethods()
|> Seq.filter(fun methodInfo ->
if methodInfo.Name = "AddTypeHandler" && methodInfo.IsGenericMethodDefinition then
let gp = methodInfo.GetParameters()
not <| isNull gp && gp.Length = 1 && gp.[0].ParameterType.Name.Contains("TypeHandler")
else false)
|> Seq.head)
.MakeGenericMethod(t)
.Invoke(null, [| ctor |]) |> ignore
)
Note:
It would have been much simpler if Dapper have had the signature of AddTypeHandler in a form ITypeHandler -> unit. But it accepts TypeHandler and in addition has overloaded version. So we need GMD for method AddTypeHandler and instantiate it with method MakeGenericMethod and then call this method with parameter which we obtains from GetConstructor ... Invoke
Playing further with reflection you can decide to mark some discriminated unions with some attribute to ignore adding the mapping. You can extend code to analyse if type has attribute. Also you can do manipulations on module basis I assume using FSharpType.IsModule
EDIT: possible solutions at bottom
I'm doing some data work where I need to be very careful about string lengths that will eventually be sent in fixed width text output, stored in limited size nvarchar fields, etc. I want to have good strict typing for these rather than naked System.String types.
Suppose I've got some code like this to represent these, with a few useful module functions that play nicely with Result.map, Option.map, etc.
module String40 =
let private MaxLength = 40
type T = private T of string
let create (s:string) = checkStringLength MaxLength s |> Result.map T
let trustCreate (s:string) = checkStringLength MaxLength s |> Result.okVal |> T
let truncateCreate (s:string) = truncateStringToLength MaxLength s |> T
let toString (T s) = s
type T with
member this.AsString = this |> toString
module String100 =
let private MaxLength = 100
type T = private T of string
let create (s:string) = checkStringLength MaxLength s |> Result.map T
let trustCreate (s:string) = checkStringLength MaxLength s |> Result.okVal |> T
let truncateCreate (s:string) = truncateStringToLength MaxLength s |> T
let toString (T s) = s
type T with
member this.AsString = this |> toString
Obviously these are almost entirely repetitive with only the module name and max length different in each block.
What options are available to try and cut down on the repetitiveness here? I would love to have something like this:
type String40 = LengthLimitedString<40>
type String100 = LengthLimitedString<100>
tryToRetrieveString () // returns Result<string, ERRType>
|> Result.bind String40.create
T4 code generation doesn't seem to be an option for F# projects
Type providers seem like overkill for this kind of simple templating, and as best as I can tell they can only produce classes, not modules.
I'm aware of Scott Wlaschin's constrained strings page, but I end up with roughly the same level of repetitive code in the 'Create a type', 'Implement IWrappedString', 'create a public constructor' steps he lists.
These code blocks are fairly short and it wouldn't be the end of the world to just copy/paste a dozen times for the different field lengths. But I feel like I'm missing a simpler way to do this.
UPDATE:
One other note is that it's important that records using these types give information about what type they're carrying:
type MyRecord =
{
FirstName: String40;
LastName: String100;
}
and not be something like
type MyRecord =
{
FirstName: LimitedString;
LastName: LimitedString;
}
Tomas' answer, using the Depended Type Provider nuget library is a pretty good one, and would be a good solution for a lot of people who are fine with its behavior as-is. I felt like it would be a little tricky to extend and customize unless I wanted to maintain my own copy of a type provider which I was hoping to avoid.
Marcelo's suggestion of static parameter constraints was a fairly productive path of research. They give me basically what I was looking for -- a generic argument that is basically an 'interface' for a static methods. However the kicker is that they require inline functions to operate and I don't have the time to evaluate how much that would or would not matter in my code base.
But I took that and altered it to use regular generic constraints. It's a bit goofy to have to instantiate an object to get a max-length value, and fsharp type/generic code is just gross to look at, but from the module users's perspective it's clean, and I can easily extend this however I want.
type IMaxLengthProvider = abstract member GetMaxLength: unit -> int
type MaxLength3 () = interface IMaxLengthProvider with member this.GetMaxLength () = 3
type MaxLength4 () = interface IMaxLengthProvider with member this.GetMaxLength () = 4
module LimitedString =
type T< 'a when 'a :> IMaxLengthProvider> = private T of string
let create< 't when 't :> IMaxLengthProvider and 't : (new:unit -> 't)> (s:string) =
let len = (new 't()).GetMaxLength()
match checkStringLength len s with
| Ok s ->
let x : T< 't> = s |> T
x |> Ok
| Error e -> Error e
let trustCreate< 't when 't :> IMaxLengthProvider and 't : (new:unit -> 't)> (s:string) =
let len = (new 't()).GetMaxLength()
match checkStringLength len s with
| Ok s ->
let x : T< 't> = s |> T
x
| Error e ->
let msg = e |> formErrorMessage
failwith msg
let truncateCreate< 't when 't :> IMaxLengthProvider and 't : (new:unit -> 't)> (s:string) =
let len = (new 't()).GetMaxLength()
let s = truncateStringToLength len s
let x : T< 't> = s |> T
x
let toString (T s) = s
type T< 'a when 'a :> IMaxLengthProvider> with
member this.AsString = this |> toString
module test =
let dotest () =
let getString () = "asd" |> Ok
let mystr =
getString ()
|> Result.bind LimitedString.create<MaxLength3>
|> Result.okVal
|> LimitedString.toString
sprintf "it is %s" mystr
I think the BoundedString type provider from the Dependent type provider project lets you do exactly what you need. Using the example from the project documentation, you can do e.g.:
type ProductDescription = BoundedString<10, 2000>
type ProductName = BoundedString<5, 50>
type Product = { Name : ProductName; Description : ProductDescription }
let newProduct (name : string) (description : string) : Product option =
match ProductName.TryCreate(name), ProductDescription.TryCreate(description) with
| Some n, Some d -> { Name = n; Description = d }
| _ -> None
I don't know how many people use this project in practice, but it seems quite simple and it does exactly what you were asking for, so it might be worth a try.
Using a touch of careful reflection magic, we can achieve a lot and get some really nice types. How about something like this?
module Strings =
type [<AbstractClass>] Length(value: int) =
member this.Value = value
let getLengthInst<'L when 'L :> Length> : 'L =
downcast typeof<'L>.GetConstructor([||]).Invoke([||])
type LimitedString<'Length when 'Length :> Length> =
private | LimitedString of maxLength: 'Length * value: string
member this.Value =
let (LimitedString(_, value)) = this in value
member this.MaxLength =
let (LimitedString(maxLength, _)) = this in maxLength.Value
module LimitedString =
let checkStringLength<'L when 'L :> Length> (str: string) =
let maxLength = getLengthInst<'L>.Value
if str.Length <= maxLength then Ok str
else Error (sprintf "String of length %d exceeded max length of %d" str.Length maxLength)
let create<'L when 'L :> Length> (str: string) =
checkStringLength<'L> str
|> Result.map (fun str -> LimitedString (getLengthInst<'L>, str))
open Strings
// === Usage ===
type Len5() = inherit Length(5)
type Len1() = inherit Length(1)
// Ok
LimitedString.create<Len5> "Hello"
// Error
LimitedString.create<Len1> "world"
One option might be to use a single module for limited-length strings that uses curried parameters for the length-limit and the string itself, then just partially apply the limit parameter. An implementation might look like this:
module LimitedString =
type T = private T of string
let create length (s:string) = checkStringLength length s |> Result.map T
let trustCreate length (s:string) = checkStringLength length s |> Result.okVal |> T
let truncateCreate length (s:string) = truncateStringToLength length s |> T
let toString (T s) = s
type T with
member this.AsString = this |> toString
Then, your modules for each length would still be required, but wouldn't have all the boilerplate:
module String100 =
let create = LimitedString.create 100
let trustCreate = LimitedString.trustCreate 100
let truncateCreate = LimitedString.truncateCreate 100
EDIT
After reading the comment and the update to the original post, I would change my suggestion a bit. Instead of defining the T type inside each module, I would have a specific struct-type single-case union for each string length at the top level. Then, I would move to toString to the individual string modules. Finally, I would add one more parameter to the LimitedString module to allow us to partially apply both the length and the specific single-case union type:
[<Struct>] type String40 = private String40 of string
[<Struct>] type String100 = private String100 of string
module LimitedString =
let create length ctor (s:string) = checkStringLength length s |> Result.map ctor
let trustCreate length ctor (s:string) = checkStringLength length s |> Result.okVal |> ctor
let truncateCreate length ctor (s:string) = truncateStringToLength length s |> ctor
module String40 =
let create = LimitedString.create 40 String40
let trustCreate = LimitedString.trustCreate 40 String40
let truncateCreate = LimitedString.truncateCreate 40 String40
let toString (String40 s) = s
module String100 =
let create = LimitedString.create 100 String100
let trustCreate = LimitedString.trustCreate 100 String100
let truncateCreate = LimitedString.truncateCreate 100 String100
let toString (String100 s) = s
type MyRecord =
{
FirstName: String40
LastName: String100
}
There's still a fair amount of boilerplate here, but I think this is in the ballpark for a solution using single-case unions and modules. A Type Provider might be possible, but you would have to consider whether the added complexity outweighs the boilerplate.
Can't you use Static Parameters, as shown on the F#.Data package's HtmlProvider example?
I have a function that takes a parameter of type object and needs to downcast it to an option<obj>.
member s.Bind(x : obj, rest) =
let x = x :?> Option<obj>
If I pass (for example) an Option<string> as x, the last line throws the exception: Unable to cast object of type 'Microsoft.FSharp.Core.FSharpOption'1[System.String]' to type 'Microsoft.FSharp.Core.FSharpOption'1[System.Object]'.
Or, if I try a type test:
member s.Bind(x : obj, rest) =
match x with
| :? option<obj> as x1 -> ... // Do stuff with x1
| _ -> failwith "Invalid type"
then x never matches option<obj>.
In order to make this work, I currently have to specify the type the option contains (e.g. if the function is passed an option<string>, and I downcast the parameter to that rather than option<obj>, the function works.
Is there a way I can downcast the parameter to option<obj> without specifying what type the option contains? I've tried option<_>, option<#obj>, and option<'a> with the same results.
By way of background, the parameter needs to be of type obj because I'm writing an interface for a monad, so Bind needs to bind values of different types depending on the monad that implements the interface. This particular monad is a continuation monad, so it just wants to make sure the parameter is Some(x) and not None, then pass x on to rest. (The reason I need the interface is because I'm writing a monad transformer and I need a way to tell it that its parameter monads implement bind and return.)
Update: I managed to get around this by upcasting the contents of the option before it becomes a parameter to this function, but I'm still curious to know if I can type-test or cast an object (or generic parameter) to an option without worrying about what type the option contains (assuming of course the cast is valid, i.e. the object really is an option).
There isn't any nice way to solve this problem currently.
The issue is that you'd need to introduce a new generic type parameter in the pattern matching (when matching against option<'a>), but F# only allows you to define generic type parameters in function declarations. So, your only solution is to use some Reflection tricks. For example, you can define an active pattern that hides this:
let (|SomeObj|_|) =
let ty = typedefof<option<_>>
fun (a:obj) ->
let aty = a.GetType()
let v = aty.GetProperty("Value")
if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
if a = null then None
else Some(v.GetValue(a, [| |]))
else None
This will give you None or Some containing obj for any option type:
let bind (x : obj) rest =
match x with
| SomeObj(x1) -> rest x1
| _ -> failwith "Invalid type"
bind(Some 1) (fun n -> 10 * (n :?> int))
I am not certain why you need to get your input as obj, but if your input is an Option<_>, then it is easy:
member t.Bind (x : 'a option, rest : obj option -> 'b) =
let x = // val x : obj option
x
|> Option.bind (box >> Some)
rest x
To answer your last question: you can use a slight variation of Tomas' code if you need a general-purpose way to check for options without boxing values beforehand:
let (|Option|_|) value =
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<option<_>> then
let opt : option<_> = (box >> unbox) value
Some opt.Value
else None
//val ( |Option|_| ) : 'a -> 'b option
let getValue = function
| Option x -> x
| _ -> failwith "Not an option"
let a1 : int = getValue (Some 42)
let a2 : string = getValue (Some "foo")
let a3 : string = getValue (Some 42) //InvalidCastException
let a4 : int = getValue 42 //Failure("Not an option")
I've been working on polishing up my JSON code for my ECMAScript runtime and I decided to run an experiment. The following str function has 4 logical steps which I've broken up into functions and marked them inline.
and private str (state:StringifyState) (key:string) (holder:IObject) : IDynamic =
let inline fourth (value:IDynamic) =
match value.TypeCode with
| LanguageTypeCode.Null ->
state.environment.CreateString "null" :> IDynamic
| LanguageTypeCode.Boolean ->
let v = value :?> IBoolean
state.environment.CreateString (if v.BaseValue then "true" else "false") :> IDynamic
| LanguageTypeCode.String ->
let v = value :?> IString
state.environment.CreateString (quote v.BaseValue) :> IDynamic
| LanguageTypeCode.Number ->
let v = value :?> INumber
if not (Double.IsInfinity(v.BaseValue))
then v.ConvertToString() :> IDynamic
else state.environment.CreateString "null" :> IDynamic
| LanguageTypeCode.Object ->
let v = value :?> IObject
let v = if v.Class = "Array" then ja state v else jo state v
state.environment.CreateString v :> IDynamic
| _ ->
state.environment.Undefined :> IDynamic
let inline third (value:IDynamic) =
match value.TypeCode with
| LanguageTypeCode.Object ->
let v = value :?> IObject
match v.Class with
| "Number" ->
fourth (v.ConvertToNumber())
| "String" ->
fourth (v.ConvertToString())
| "Boolean" ->
fourth (v.ConvertToBoolean())
| _ ->
fourth value
| _ ->
fourth value
let inline second (value:IDynamic) =
match state.replacerFunction with
| :? ICallable as f ->
let args = state.environment.CreateArgs ([| state.environment.CreateString key :> IDynamic; value |])
let value = f.Call (state.environment, holder :> IDynamic, args)
third value
| _ ->
third value
let inline first (value:IDynamic) =
match value with
| :? IObject as v ->
let toJSON = v.Get "toJSON"
match toJSON with
| :? ICallable as f ->
let args = state.environment.CreateArgs ([| state.environment.CreateString key :> IDynamic |])
let value = f.Call (state.environment, value, args)
second value
| _ ->
second value
| _ ->
second value
first (holder.Get key)
I compiled with full optimizations and opened up the resulting assembly with Reflector to see the results.
[CompilationArgumentCounts(new int[] { 1, 1, 1 })]
internal static IDynamic str(StringifyState state, string key, IObject holder)
{
IObject obj3;
ICallable callable;
ICallable callable2;
IArgs args;
IDynamic dynamic3;
IDynamic dynamic4;
ICallable callable3;
IDynamic dynamic5;
IBoolean flag;
IString str;
INumber number;
IObject obj4;
string str2;
INumber number2;
IObject obj5;
string str3;
IString str4;
IBoolean flag2;
IDynamic thisBinding = holder.Get(key);
IObject obj2 = thisBinding as IObject;
if (obj2 == null)
{
callable = state.replacerFunction# as ICallable;
if (callable == null)
{
switch (thisBinding.TypeCode)
{
case LanguageTypeCode.Object:
obj3 = (IObject) thisBinding;
str2 = obj3.Class;
if (!string.Equals(str2, "Number"))
{
if (string.Equals(str2, "String"))
{
dynamic3 = obj3.ConvertToString();
switch (dynamic3.TypeCode)
{
case LanguageTypeCode.Null:
return (IDynamic) state.environment#.CreateString("null");
case LanguageTypeCode.Boolean:
flag = (IBoolean) dynamic3;
return (IDynamic) state.environment#.CreateString(!flag.BaseValue ? "false" : "true");
case LanguageTypeCode.String:
str4 = (IString) dynamic3;
return (IDynamic) state.environment#.CreateString(quote(str4.BaseValue));
case LanguageTypeCode.Number:
number = (INumber) dynamic3;
if (double.IsInfinity(number.BaseValue))
{
return (IDynamic) state.environment#.CreateString("null");
}
return (IDynamic) number.ConvertToString();
// ... I removed a large amount of code.
return (IDynamic) state.environment#.Undefined;
}
Clearly the inline modifier is quite literal. The code is quite huge and with some preliminary tests is very efficient. One might consider throwing inline on all of their functions if they didn't care about the size of the resulting assemblies. What are some guidelines I can follow to know when the use of inline is appropriate? If possible I would like to avoid having to measure performance every single time to determine this.
If you are using inline solely for performance considerations, then I think that all of the typical performance-related advice applies. Most importantly, set a performance target and profile your application for hotspots. Then use inline if you have reason to believe that it will improve performance, and test to verify that it does. Keep in mind that the IL that the F# compiler generates is JIT compiled anyway, so small functions (in terms of IL size) may be inlined in the compilation to machine code even if you don't use inline in your F# code.
I typically only use inline when I want to use statically resolved type variables (e.g. because of member constraints).
I agree with kvb's answer, but here are two specific reasons not to
consider throwing inline on all of their functions if they didn't care about the size of the resulting assemblies.
The obvious case is that inlining anonymous functions won't work.
More inlining (especially of big functions) -> less (effectively) code fits into cache -> the program works slower.
What is the F# equivalent of the following C# code? Specifically, I need to check if an event is being handled.
protected virtual void OnClicked(ClickEventArgs e) {
if (this.Clicked != null) //how can I perform this check in F#
this.Clicked(this, e);
}
Okay, I think I figured this thing out. Taking a cue from Don Syme's blog, specifically the section "The Implementation of the IEvent Module."
Instead of the following:
let validationFailedEvent = new Event<DataValidationEventHandler, DataValidationEventArgs>()
I had to implement IEvent myself and create a variable to hold the invocation list:
let mutable listeners: Delegate = null
let validationFailedEvent = { new IEvent<DataValidationEventHandler, DataValidationEventArgs> with
member x.AddHandler(d) =
listeners <- Delegate.Combine(listeners, d)
member x.RemoveHandler(d) =
listeners <- Delegate.Remove(listeners, d)
member x.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(x :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (x :?> IEvent<_,_>).RemoveHandler(h) } }
Then, to check if there are listeners, and, if not, raise an exception:
member private x.fireValidationFailedEvent(e:DataValidationEventArgs) =
match listeners with
| null -> failwith "No listeners"
| d -> d.DynamicInvoke([| box x; box e |])
An alternative way to implement RequiresSubscriptionEvent is to build on top of the existing Event functionality (using composition) and just add a counter that counts the number of registered handlers and add a property HasListeners (or even publish the number of listeners if you wanted...)
This makes the code a bit easier to use and hopefuly also safer, because if you don't check whether it has any listneres, it will still work as the usual F# code. And if you want to perform the check, you can...
type RequiresSubscriptionEvent<_>() =
let evt = new Event<_>()
let mutable counter = 0
let published =
{ new IEvent<_> with
member x.AddHandler(h) =
evt.Publish.AddHandler(h)
counter <- counter + 1;
member x.RemoveHandler(h) =
evt.Publish.RemoveHandler(h)
counter <- counter - 1;
member x.Subscribe(s) =
let h = new Handler<_>(fun _ -> s.OnNext)
x.AddHandler(h)
{ new System.IDisposable with
member y.Dispose() = x.RemoveHandler(h) } }
member x.Trigger(v) = evt.Trigger(v)
member x.Publish = published
member x.HasListeners = counter > 0
Sample usage:
type Demo() =
let evt = new RequiresSubscriptionEvent<_>()
[<CLIEvent>]
member x.OnSomething = evt.Publish
member x.FooThatFiresSomething() =
if evt.HasListeners then
evt.Trigger("foo!")
else
printfn "No handlers!"
Even though this isn't a part of standard F# libraries, it shows the great advantage of F# first class events. If there is some missing functionality, you can simply implement it yourself!
Typically, you don't need to do that check in F# (the event infrastructure checks for you):
type T() =
let ev = new Event<_>()
[<CLIEvent>]
member x.Event = ev.Publish
member x.OnClicked() =
ev.Trigger()
I followed kvb's suggestion and put this logic in a class. I copied Event from the F# sources and added a Handled property, which checks if the Delegate is null. I tried adding to, then removing handlers from the event to make sure it gets set back to null, and indeed it does.
type EventEx<'Delegate,'Args when 'Delegate : delegate<'Args,unit> and 'Delegate :> System.Delegate >() =
let mutable multicast : System.Delegate = null
static let argTypes =
let instanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
let mi = typeof<'Delegate>.GetMethod("Invoke",instanceBindingFlags)
mi.GetParameters() |> (fun arr -> arr.[1..]) |> Array.map (fun p -> p.ParameterType)
member x.Handled = (multicast <> null)
member x.Trigger(sender:obj,args:'Args) =
match multicast with
| null -> ()
| d ->
if argTypes.Length = 1 then
d.DynamicInvoke([| sender; box args |]) |> ignore
else
d.DynamicInvoke(Array.append [| sender |] (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(box args))) |> ignore
member x.Publish =
{ new IEvent<'Delegate,'Args> with
member x.AddHandler(d) =
multicast <- System.Delegate.Combine(multicast, d)
member x.RemoveHandler(d) =
multicast <- System.Delegate.Remove(multicast, d)
member e.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(e :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (e :?> IEvent<_,_>).RemoveHandler(h) } }
This article here http://geekswithblogs.net/Erik/archive/2008/05/22/122302.aspx says you do not need to check for null events in F#, though I don't know what his reference is.
This article http://blogs.msdn.com/dsyme/articles/FSharpCompositionalEvents.aspx by Don Symes goes into F# events in quite a bit of detail. It looks like events are not owned by the class in F#
From the above,
it is that events are now first-class
values in the F# langauge. Indeed,
events are not a separate notion at
all in the language design, rather,
events are just values of type
Microsoft.FSharp.Idioms.IEvent<_>, and
.NET events are effectively just
properties of this type.
And
One of the restrictions of C# is that
events can only exist as members
within classes. With the F# model,
new event values can be created just
as values as part of any expression.