The problem is that the method object held by the .NET delegate is a read-only object. It cannot be overwritten. No problem, we'll just indirect through the field we can write. But we need to pass in the delegate object itself as an argument so we can modify it. In order to do that the delegate method needs access to the
this
pointer in the delegate. But the point of a delegate is to provide the delegated method with a this
pointer. That is the Target
field in the delegate. But the target object held by the .NET delegate is also read-only. It isn't possible to construct a delegate that delegates to itself in .NET (at least not without major black magic). We can create a delegate that delegates to a static method within the delegate class itself, but then we no longer have access to the instance object.The solution is another level of indirection. We create a
ManifestInstance
class that has mutable state we need and closes over itself. The delegate simply defers to the ManifestInstance
. This is the way delegates are intended to be used, so there is no need to use IL to add extra fields or anything. We can do this with standard, simple C#.But I still want my
StandardObjects
to be printed with some sort of useful debugging info rather than {Method = {System.Object uninitializedFuncallableInstanceMethod(System.Object[])}}
. The StandardObject
delegate needs a DebuggerTypeProxy
. A DebuggerTypeProxy
is a helper object that tells the debugger how to render objects when the standard mechanisms aren't sufficient. But for reasons I don't understand, you can't associate a DebuggerTypeProxy
with a .NET delegate! Argh!!!!But I found a trick. You can associate a
DebuggerDisplay
attribute with a delegate. This is a string attribute that is interpreted in the context of the object being debugged. You can put things like "Object {Name}"
in the DebuggerDisplay attribute and it will print the literal string `Object
' followed by the Name
field of the object. For our StandardObject
delegate, we tell it to print the Target
field. This sort of works: "{Target}"
, but then the debugger uses the ToString
method of the ManifestInstance
. It makes it hard to visually distinguish the ManifestInstance
from the delegate that refers to it. Instead, we want to get a computed property of the ManifestInstance
. Unfortunately, this doesn't work: "{Target.ObjectDebuggerDisplay}"
, but this does: "{((ManifestInstance)Target).ObjectDebuggerDisplay}"
. Now when the debugger sees a StandardObject
, it calls a method defined in ManifestInstance
that has access to the object's state.Since all this can be done in simple C# code, there is no reason to hack IL for
StandardObject
s. Back to the hacking....
No comments:
Post a Comment