tags:

views:

841

answers:

3

I am designing a helper method that does lazy loading of certain objects for me, calling it looks like this:

public override EDC2_ORM.Customer Customer {
    get { return LazyLoader.Get<EDC2_ORM.Customer>(
            CustomerId, _customerDao, ()=>base.Customer, (x)=>Customer = x); }
    set { base.Customer = value; }
}

when I compile this code I get the following warning:

Warning 5 Access to member 'EDC2_ORM.Billing.Contract.Site' through a 'base' keyword from an anonymous method, lambda expression, query expression, or iterator results in unverifiable code. Consider moving the access into a helper method on the containing type.

What exactly is the complaint here and why is what I'm doing bad?

+2  A: 

copy/pasting from here:

Codesta: C#/CLR has 2 kinds of code, safe and unsafe. What is it trying to provide and how did this affect the virtual machine? Peter Hallam: For C# the terms are safe and unsafe. The CLR uses the terms verifiable and unverifiable.

When running verifiable code the CLR can enforce security policies; the CLR can prevent verifiable code from doing things that it doesn't have permission to do. When running potentially malicious code, code that was downloaded from the internet for example, the CLR will only run verifiable code, and will ensure that the untrusted code doesn't access anything that it doesn't have permission to access.

The use of standard C style pointers creates unverifiable code. The CLR supports C style pointers natively. Once you've got a C style pointer you can read or write to any byte of memory in the process, so the runtime cannot enforce security policy. Actually it could but the performance penalty would make it impractical.

Now, that does not fully answer your question (i.e. WHY is this now unverifiable code), but at least it explains that "unverifiable" is the CLR-term for "unsafe". I assume that anonymous methods and base classes result in some funky pointer-magic internally.

By the Way: I think that the code snippet does not match the Warning message. The code is talking about a Customer, the Warning is about the Billing. Is it possible to post the actuon code the warning is generated for? Maybe you have something else in that code that would better explain why you get the warning.

Michael Stum
Assuming I've guessed correctly, it's not really doing any funky pointer magic - but it's using a non-virtual call to a virtual member, which I suspect you're only allowed to do (verifiably) from within a derived type.
Jon Skeet
Cool. Three new things learned during this question
Michael Stum
@Jon, this is correct. It's an ... interesting restriction the CLR added in 2.0
JaredPar
+7  A: 

I suspect the problem is that you're basically saying, "I don't want to use the most derived implementation of Customer - I want to use this particular one" - which would you wouldn't be able to do normally. You're allowed to do it within a derived class, and for good reasons, but from other types you'd be violating encapsulation.

Now, when you use an anonymous method, lambda expression, query expression (which basically uses lambda expressions) or iterator block, sometimes the compiler has to create a new class for you behind the scenes. Sometimes it can get away with creating a new method in the same type for lambda expressions, but it depends on the context. Basically if any local variables are captured in the lambda expression, that needs a new class (or indeed multiple classes, depending on scope - it can get nasty). If the lambda expression only captures the this reference, a new instance method can be created for the lambda expression logic. If nothing is captured, a static method is fine.

So, although the C# compiler knows that really you're not violating encapsulation, the CLR doesn't - so it treats the code with some suspicion. If you're running under full trust, that's probably not an issue, but under other trust levels (I don't know the details offhand) your code won't be allowed to run.

Does that help?

Jon Skeet
So, to correct this, the easiest way would be to wrap the call to the base implementation in another member method, and then call that either as a delegate or via a lambda?
Jeff Yates
Yes that's very helpful. I wasn't aware that lambdas created new classes, though I suppose that it makes perfect sense.
George Mauer
Lambdas basically create new classes when they have to for the sake of capturing local variables. Capturing "this" can be done by using an instance method for the delegate. If no capturing is required, a static method can be used.
Jon Skeet
@Jon, Lambads don't necessarily create a new class. If the lambda can be achieved with a static or instance method no new class will be created. Creates weird behavior in C# 2.0 as base.Foo will actually work in some cases. I believe it was made a flat out error in 3.0
JaredPar
Jared: Hence the "sometimes it can get away with" bit :) I'll edit the answer to make it clearer though.
Jon Skeet
+13  A: 

"base.Foo" for a virtual method will make a non-virtual call on the parent definition of the method "Foo". Starting with CLR 2.0, the CLR decided that a non-virtual call on a virtual method can be a potential security hole and restricted the scenarios in which in can be used. They limited it to making non-virtual calls to virtual methods within the same class hierarchy.

Lambda expressions put a kink in the process. Lambda expressions often generate a closure under the hood which is a completely separate class. So the code "base.Foo" will eventually become an expression in an entirely new class. This creates a verification exception with the CLR. Hence C# issues a warning.

Side Note: The equivalent code will work in VB. In VB for non-virtual calls to a virtual method, a method stub will be generated in the original class. The non-virtual call will be performed in this method. The "base.Foo" will be redirected into "StubBaseFoo" (generated name is different).

JaredPar
Does it only do that when the lambda expression is being converted into a delegate rather than an expression tree? Otherwise you could end up with some weird expression trees...
Jon Skeet
Yes, this will only occur in VB when it's a lambda vs. an expression tree lambda.
JaredPar
@Jared: Since the VB compiler does it right, can this be considered a bug in the C# compiler? Will this be fixed? Also, can you illustrate in what contexts non-virtual calls to virtual functions might be a security hole? I can't imagine any such scenario.
Konrad Rudolph
@Konrad, I'm not sure if they would consider it a bug or "by design." It can be a real thin line. It's very common in VB (often in samples) for users to write MyBase. or MyClass. When the topic of non-virtual in a closure came up we decided that it was better to implement than re-educate the user
JaredPar
@Konrad, on the verification issue, check out this blog post http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=c33b0dbc-a696-4b3d-a136-4bee2d86be2a
JaredPar