Thursday, October 11, 2018

Making the Dynamic Operator More ... Dynamic

So in F# you can (and have to if you want it) make a dynamic operator implementation.

Like so:
    let (?) (this : 'Source) (prop : string) : 'Result =
        let t : Type = this.GetType()
        let p : PropertyInfo = t.GetProperty(prop)
        if isNull p then failwithf "could not find prop %s" prop
        p.GetValue(this, null) :?> 'Result
Or dynamic setters even:
    let (?<-) (this: 'Source) (prop:string) (value: 'Value) =
        this.GetType().GetProperty(prop).SetValue(this,value,null)
Which are accessed by name not by string (even though the effect is like a magic string):
    type Test(code:string) = 
        member val Code = code with get,set
    let nakedTest : Test = Test("hello")
    let x : obj = box nakedTest
    let value : string = x?Code
    printfn "I found the property and retrieved the value, it was %s" value

Or the setter like: x?Code <- "dynamic"

Now what happens if I want part of that implementation to be variable, I want to pass different combinations of BindingFlag bits, and the keep the rest of the implementation the same?

Perhaps I want to access static or private properties, with different (?) implementations.

Well, it's possible!


    let makePropAccess (flags:BindingFlags) =
        let (?) (this : 'Source) (prop : string) : 'Result =
            let t = this.GetType()
            if isNull t then failwithf "bad getType"
            let p = t.GetProperty(prop,flags)
            if isNull p then failwithf"could not find prop %s" prop
            p.GetValue(this, null) :?> 'Result
        (?)
        
    let (?) = makePropAccess (BindingFlags.Public ||| BindingFlags.Instance) 
    let x = Test("Code")    
    x?Code
full LINQPad code at Github.com/ImaginaryDevelopment/LinqPad/LINQPad Queries/Reflection/F# dynamic operator implementation.linq

I wonder how (or if) you could even access this from C#