Cast MethodBase to RuntimeMethodInfo in F# - reflection

I want to cast MethodBase to RuntimeMethodInfo in order to retrieve the name and type of the arguments of reflected methods, and the returned type of those methods.
I can make a direct cast in Inmediate Window but have not found a way to make the cast with F#.

Why cast? You can call GetParameters() and all the other members you'd need on the MethodBase reference.
let methodInfo : MethodBase = //whatever
let firstParamName = methodInfo.GetParameters().[0].Name
EDIT (return types):
First, note that GetMethod returns MethodInfo, not MethodBase. You can't cast to RuntimeMethodInfo since, as others have noted, that is an internal type. But the ReturnType property is declared on MethodInfo, so all is well.
This therefore works, with the static type of methodInfo being MethodInfo:
let methodInfo = typeof<object>.GetMethod("ToString")
let returnTypeName = methodInfo.ReturnType.Name // "String"
Second, if you have a static reference to a MethodBase that you know is a MethodInfo, use the :?> operator. Example:
let toMethodInfo (mb : MethodBase) = mb :?> MethodInfo
On the other hand, if you're not sure about the object's actual type, you can use match:
let tryToMethodInfo (mb : MethodBase) =
match mb with
| :? MethodInfo as result -> Some result
| _ -> None
Finally, since you ask about "vice versa" in your comment: When you're going from a derived class to one of its base classes, which always succeeds, you don't need the question mark:
let toMethodBase (mi : MethodInfo) = mi :> MethodBase

Related

why do I need parenthesis in this dictionary initializer, in F#

with these types:
type A =
| AA
| AB
type B =
Dictionary<int, int>()
I initialize a dictionary:
Dictionary<A, B>(dict [ (A.AA, B()); (A.AB, B()) ])
but I do not understand why I need to put parenthesis after B, in the initialization code.
the following:
Dictionary<A, B>(dict [ (A.AA, B); (A.AB, B) ])
will not compile. I understand that 'B' may represent the type and 'B()' an instance of it, but I don't understand why the '()' would represent an instance?
As an additional question:
type B =
Dictionary<int, int>()
and
type B =
Dictionary<int, int>
both seem to work. Is any of the two preferred, and, if so, why?
First of all, the declaration type B = Dictionary<int, int>() does not work for me. I get an error "Unexpected symbol '(' in member definition", exactly as I would expect. Are you sure it's working for you? Which version of F# are you using?
The type Dictionary<_,_> is a class. Classes are not the same as discriminated unions (which the type A is). They are a different sort of type.
In particular, to create a value of a class type, one needs to call a constructor and pass it some parameters. This is exactly what you're doing in your very own code:
Dictionary<A, B> (dict [ (A.AA, B()); (A.AB, B()) ])
^--------------^ ^---------------------------------^
| |
constructor |
|
parameter passed to the constructor
Some classes have multiple constructors. Dictionary is one of such types.
Some constructors have no parameters, but you still have to call them. This is what you do with empty parens.
F# models parameterless .NET methods and constructors as functions that have a single parameter, and that parameter is of type unit. This is what you're doing when you say B()
B ()
^ ^^
| |
| single parameter of type unit
|
constructor
If you just say B without a parameter, then what you get is a function of type unit -> B - that is a function that expects a single parameter of type unit and when you pass it such parameter, it would return you a value of type B.

How do I check 'T for types not being allowed to be null?

given
let inline deserialize<'t> x :'t option =
printfn "Attempting to deserialize %A" typeof<'t>.Name
try
JsonConvert.DeserializeObject<'t>(x)
|> Some
with ex ->
System.Diagnostics.Trace.WriteLine(sprintf "Error deserialization failed:%s" ex.Message)
None
is returning for example an obj list as null. FSharpList<_> is not allowed to be null. How can I, without knowing what 't is ask F# if the type I'm about to return supports null so that I can halt/throw/act accordingly? Is there a reflection flag or Microsoft.FSharp.Reflection... method for this?
The full answer involves checking if the type is a record (in which case null is never allowed), or if it's a union (in which case null is allowed if the type has a CompilationRepresentation CustomAttribute whose flags contain the UseNullAsTrueValue member (https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/core.compilationrepresentationflags-enumeration-%5Bfsharp%5D for more details)).
To answer the first question you can use the IsRecord function in the FSharpType module (https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/reflection.fsharptype-class-%5Bfsharp%5D) and to answer the second you can use a combination of the IsUnion function on that same module and CustomAttribute hunting.
In case the type is a union with UseNullAsTrueValue set, you should be good to go, just send the value along.
Best I can think of is to box the result (in case you are deserializing structs) and pattern match it with null:
let inline deserialize<'t> x :'t option =
printfn "Attempting to deserialize %A" typeof<'t>.Name
try
let obj = Newtonsoft.Json.JsonConvert.DeserializeObject<'t>(x)
match box obj with
| null -> None
| _ -> Some obj
with ex ->
System.Diagnostics.Trace.WriteLine(sprintf "Error deserialization failed:%s" ex.Message)
None
let r1 = deserialize<obj list> ("[1,2,3]") //val r1 : obj list option = Some [1L; 2L; 3L]
let r2 = deserialize<obj list> ("null") //val r2 : obj list option = None

Assign value to optional dictionary in Swift

I'm finding some surprising behavior with optional dictionaries in Swift.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else {
// Following line errors with "'Dictionary<String, String>?' does
// not have a member named 'subscript'"
foo["qux"] = "quux"
}
I've played with this a lot, trying to figure out what I might be missing, but nothing seems to make this code work as expected short of making the dictionary not optional. What am I missing?
The closest I can get is the following, but of course it's ridiculous.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
}
The lightbulb moment is when you realize that an Optional dictionary is not a Dictionary. An Optional anything is not that thing! It is an Optional!! And that's all it is. Optional is itself a type. An Optional is just an enum, wrapping the possible cases nil and some value. The wrapped value is a completely different object, stored inside.
So an Optional anything does not act like the type of that thing. It is not that thing! It is just an Optional. The only way to get at the thing is to unwrap it.
The same is true of an implicitly unwrapped Optional; the difference is just that the implicitly unwrapped Optional is willing to produce (expose) the wrapped value "automatically". But it is still, in fact, wrapped. And, as Bryan Chen has observed, it is wrapped immutably; the Optional is just holding it for you - it is not giving you a place to play with it.
you can use this code
if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
} else {
foo = ["bar": "baz"]
}
with this code
var foo:Dictionary<String, String>? = Dictionary()
foo[""]=""
error: 'Dictionary<String, String>?' does not have a member named 'subscript'
foo[""]=""
^
the error message makes sense to me that Dictionary<String, String>? does not implement subscript method, so you need to unwrap it before able to use subscript.
one way to call method on optional is use ! i.e. foo![""], but...
var foo:Dictionary<String, String>? = Dictionary()
foo![""]=""
error: could not find member 'subscript'
foo![""]=""
~~~~~~~~^~~
whereas
var foo:Dictionary<String, String>? = Dictionary()
foo![""]
works
it is interesting these code failed to compile
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo[""]=""
error: could not find an overload for 'subscript' that accepts the supplied arguments
foo[""]=""
~~~~~~~^~~
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo.updateValue("", forKey: "")
immutable value of type 'Dictionary<String, String>' only has mutating members named 'updateValue'
foo.updateValue("", forKey: "")
^ ~~~~~~~~~~~
the last error message is most interesting, it is saying the Dictionary is immutable, so updateValue(forKey:) (mutating method) can't be called on it
so what happened is probably that the Optional<> store the Dictionary as immutable object (with let). So even Optional<> it is mutable, you can't modify the underlying Dictionary object directly (without reassign the Optional object)
and this code works
class MyDict
{
var dict:Dictionary<String, String> = [:]
subscript(s: String) -> String? {
get {
return dict[s]
}
set {
dict[s] = newValue
}
}
}
var foo:MyDict? = MyDict()
foo!["a"] = "b" // this is how to call subscript of optional object
and this lead me to another question, why Array and Dictionary are value type (struct)? opposite to NSArray and NSDictionary which are reference type (class)
This is because your Dictionary is optional. If it's nil, you won't add an entry to it.
You can do this way:
var dict: [String : String]?
if let dict = dict {
dict["key"] = "value" // add a value to an existing dictionary
} else {
dict = ["key" : "value"] // create a dictionary with this value in it
}
Or, if you are given an optional dictionary, for example HTTPHeaders - which in AlamoFire is a [String : String] dictionary - and you want to either add a value if it's non-nil, or create it with this value if it's nil, you could do like so:
let headers: HTTPHeaders? // this is an input parameter in a function for example
var customHeaders: HTTPHeaders = headers ?? [:] // nil coalescing
customHeaders["key"] = "value"
I tried this for Swift 3.1 and it worked:
if (myDict?[key] = value) == nil {
myDict = [key: value]
}

How should I expose a global Dictionary declared in f# that will have items added from different HttpModules?

I have a dictionary (formatters) declared in the following code that will have items added to it inside of multiple HttpModules. Once those are loaded it will not be written to again. What would be the best way to expose this so it can be accessed from any .NET language? I know this seems lame and looks like I should just have them implement ToString() however part of the application requires strings to be in a certain format and I don't want clients having to implement ToString() in a way that is specific to my application.
module MappingFormatters
open System
open System.Collections.Generic
let formatters = new Dictionary<Type, obj -> string>();
let format item =
let toDateTime (d:DateTime) =
let mutable date = d;
if (date.Kind) <> System.DateTimeKind.Utc then
date <- date.ToUniversalTime()
date.ToString("yyyy-MM-ddTHH:mm:00Z")
let stripControlCharacters (str:string) =
let isControl c = not (Char.IsControl(c))
System.String( isControl |> Array.filter <| str.ToCharArray())
let defaultFormat (item:obj) =
match item with
| :? string as str-> stripControlCharacters(str)
| :? DateTime as dte -> toDateTime(dte)
| _ -> item.ToString()
let key = item.GetType();
if formatters.ContainsKey(key) then
formatters.Item(key) item
else
defaultFormat item
If the question is just one about language interoperability, then I think you should just change the type from
Dictionary<Type, obj -> string>
to
Dictionary<Type, Func<obj, string> >
and then you should be in good shape.
After researching. I have decided to create a type called MappingFormatters to hold the method for adding the formatter. The client does not need to call it, but my f# code will. I believe this will let me use the common f# conventions while exposing a way for other .net languages to inter-operate with the least confusion.
module File1
open System
let mutable formatters = Map.empty<string, obj -> string>
let format (item:obj) =
let dateToString (d:DateTime) =
let mutable date = d;
if (date.Kind) <> System.DateTimeKind.Utc then
date <- date.ToUniversalTime()
date.ToString("yyyy-MM-ddTHH:mm:00Z")
let stripCtrlChars (str:string) =
let isControl c = not (Char.IsControl(c))
System.String( isControl |> Array.filter <| str.ToCharArray())
let key = item.GetType().AssemblyQualifiedName
if Map.containsKey key formatters then
Map.find key formatters item
else
match item with
| :? DateTime as d -> dateToString d
| _ -> stripCtrlChars (item.ToString())
let add (typ:Type) (formatter:obj -> string) =
let contains = Map.containsKey
let key = typ.AssemblyQualifiedName
if not (formatters |> contains key) then
formatters <- Map.add key formatter formatters
type MappingFormatters() = class
let addLock = new obj()
member a.Add (``type``:Type, formatter:Func<obj,string>) =
lock addLock (fun () ->
add ``type`` (fun x -> formatter.Invoke(x))
)
end

Upcasting F# record created through reflection

I've been messing about with F# and it's Reflection, trying to create a Record type object dynamically from within F#, I got most of it working (as you can see below) but one thing - the record I create through reflection has type "obj" instead the one it should ("Person") and I can't seem to be able to upcast it in any way.
#light
type Person = {
Name:string;
Age:int;
}
let example = {Name = "Fredrik"; Age = 23;}
// example has type Person = {Name = "Fredrik"; Age = 23;}
let creator = Reflection.FSharpValue.PrecomputeRecordConstructor(example.GetType(),
System.Reflection.BindingFlags.Public)
let reflected = creator [| ("thr" :> obj); (23 :> obj) |]
// here reflected will have the type obj = {Name = "thr"; Age = 23;}
// Function that changes the name of a Person record
let changeName (x:Person) (name:string) =
{ x with Name = name }
// Works with "example" which is has type "Person"
changeName example "Johan"
// But not with "reflected" since it has type "obj"
changeName reflected "Jack" // Error "This expression has type obj but is here used with type Person. "
// But casting reflected to Person doesn't work either
(reflected :> Person) // Type constraint mismatch. The type obj is not compatible with
// type Person. The type 'obj' is not compatible with the type 'Person'.
// C:\Users\thr\Documents\Visual Studio 2008\Projects\
// Reflection\Reflection\Script.fsx 34 2 Reflection
Try using the other cast operator (as you're casting the other way this time)
So changeName (reflected :?> Person) "Jack"

Resources