F# under the covers XVI -- Constructor weirdness
Occasionally, even in F#, one needs to do OO stuff, like implementing a concrete subclass of some abstract framework type to feed into some other framework API.  In my case, I recently needed to add a SerializationBinder to a BinaryFormatter to handle assembly versioning.
So of course I wrote
formatter.Binder <- { new System.Runtime.Serialization.SerializationBinder()
  with member self.BindToType (a:string, t:string) = ... }which worked perfectly happily, but threw up a warning from Gendarme about suspicious recursion in the constructor.
So I decompiled the type to find it looked like
[Serializable]
[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
[CompilationMapping(SourceConstructFlags.Closure)]
internal sealed class ReadResults@64 : SerializationBinder
{
 public ReadResults@64()
 {
  ((SerializationBinder)this)..ctor();
 }
 public override Type BindToType(string _arg1, string _arg2)
 {
  ...
 }
}or, as IL
.method public specialname rtspecialname 
 instance void .ctor () cil managed 
{
 // Method begins at RVA 0x3ce0
 // Code size 9 (0x9)
 .maxstack 8
 IL_0000: ldarg.0
 IL_0001: callvirt instance void [mscorlib]System.Runtime.Serialization.SerializationBinder::.ctor()
 IL_0006: ldarg.0
 IL_0007: pop
 IL_0008: ret
}Writing the type as an explicit class, like
type UpdateBinder () = inherit System.Runtime.Serialization.SerializationBinder() override self.BindToType ...
yields exactly the same sort of IL.
Revising the class yet again as
type MonoTypeBinder (``type``:Type) =
  inherit System.Runtime.Serialization.SerializationBinder()
  override self.BindToType (_:string, _:string) =
    ``type``because I only have one type of interest, did produce the expected decompiled constructor, looking like
public MonoTypeBinder(Type type)
  : this()
 {
  this.type = type;
 }even though the actual IL just adds the field assignment
IL_0000: ldarg.0 IL_0001: callvirt instance void [mscorlib]System.Runtime.Serialization.SerializationBinder::.ctor() IL_0006: ldarg.0 IL_0007: pop IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: stfld class [mscorlib]System.Type AltCover.MonoTypeBinder::'type' IL_000f: ret
However, now we've changed the signature, the call no longer looks like a recursion. And, for once, this is a case where the virtual call in a constructor is safe.
By contrast, a C# equivalent
class UpdateBinder : System.Runtime.Serialization.SerializationBinder
    {
        public override Type BindToType(string a, string t)
        {
            ...
        }
    }generates a default constructor with IL that makes a non-virtual call to the base type
IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Runtime.Serialization.SerializationBinder::.ctor() IL_0006: nop IL_0007: ret
and the call remains non-virtual when adding a constructor argument.
 
 
 Posts
Posts
 
 

No comments :
Post a Comment