Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
I'm still learning my way around the C# codebase – heck, I'm still learning my way around the Jscript codebase and I've been working on it for nine years, not nine weeks. Here's something I stumbled across while refactoring the anonymous method binding code last week that I thought might be interesting to you folks.
Consider this simple program:
using System;public delegate void D ();public class Alpha { public virtual void Blah() { Console.WriteLine("Alpha.Blah"); }}public class Bravo : Alpha { public override void Blah() { Console.WriteLine("Bravo.Blah"); base.Blah(); }}
Pretty straightforward so far. Any virtual call to
public void Charlie() { int x = 123; D d = delegate { this.Blah(); base.Blah(); Console.WriteLine(x); }; d(); }
When you compile this thing up you get a crazy-sounding warning:
warning CS1911: Access to member 'Alpha.Blah()' through a 'base' keyword from an anonymous method or iterator results in unverifiable code. Consider moving the access into a helper method on the containing type.
And indeed, if you compile this up and run it through PEVERIFY.EXE, sure enough you'll get an unverifiable code warning. Unverifiable code requires full trust and is generally to be avoided – what is going on here?
This is an artefact of the way that C# realizes anonymous delegates. The anonymous delegate above captures both
public class Bravo : Alpha { public override void Blah() { Console.WriteLine("Bravo.Blah"); base.Blah(); } private class __locals { public int __x; public Bravo __this; public void __method() { this.__this.blah();
Of course there is no such thing in C# as
That's a pretty suspicious programming practice. People who build object hierarchies that manipulate sensitive information have a reasonable expectation that the only way to call a base class implementation of a virtual method is from the derived class itself, not from some third-party method. Therefore the CLR treats this as unverifiable code, and therefore we have to issue a warning. (I suppose the CLR team could have made an exception for nonvirtual references from classes scoped to within the class in question, or for that matter, classes from the same assembly, but they didn't.)
Now, I was refactoring the code that generates the captured variable class, and so of course I wanted to write a few additional unit tests to make sure that I wasn't breaking anything. When I ran into this warning the first unit test that I wrote was actually a somewhat simplified version of the above:
public void Charlie() { D d = delegate { this.Blah(); base.Blah(); }; d(); }
This issues the warning above, but when I ran the generated executable through PEVERIFY I was surprised to discover that it verified just fine.
As it turns out, the compiler is clever about this one. It sees that the only captured variable is the
We decided to issue the warning even in this case because we thought that it would make C# seem really weird and brittle to suddenly start issuing the warning when you add an additional outer local variable reference to the anonymous delegate. Even when this doesn't actually generate nonverifiable code, it's a good idea to get in the habit of creating a helper method on the real class that does the nonvirtual access.
Of course, all of the stuff above is implementation details. You cannot rely upon future versions of C# to continue to generate anonymous methods in this manner. We probably will, but who knows what new features will be added to the CLR that might make it possible to not generate a bunch of hidden classes behind the scenes to do this work? Please do not attempt to do anything silly like reflecting upon the class to discover the hidden nested classes and use them; you're just asking for future pain if you do.
Sorry. Obviously I was not clear.
What I am describing here is a BUG in the compiler, the reason for that bug, and a description of how you could work around that bug.
It was certainly NEVER our _intention_ to force you to write these pointless and irritating helper methods. Our intention is that it just works; we did not meet our goal in this particular case.
I was hoping to have this bug fixed for C# 3.0 but unfortunately it did not make it in. Hopefully we will have it fixed in a future service pack.
Eric says this is fixed in C# v4:
http://blogs.msdn.com/ericlippert/archive/2008/11/07/the-future-of-c-part-five.aspx
in the comments:
> Re: calling base methods from anonymous methods: Yes, we fixed that.
Fixed in C# v4... Except when using generics:
connect.microsoft.com/.../badimageformatexception-on-simple-program-using-generics-and-lambdas