Error in C#: “an expression tree may not contain a

2019-03-09 06:47发布

问题:

I was calling a method that accepts Expression<Func<bool>>.

As part of the expression I was passing:

this.Bottom == base.lineView.Top

The compiler gave me an error that

an expression tree may not contain a base access

So I simply changed it to

this.Bottom == this.lineView.Top

because the member was protected anyway and now it works.

But this error really got me: why the heck would this base be a problem? Especially if using this instead will work but syntactically be the same result (same variable gets accessed)?

回答1:

Looking at the System.Linq.Expressions.Expression documentation, I don't think there's an expression type which represents "base member access". Don't forget that even though in your case it meant the same as just this, in other cases it wouldn't:

class Test
{
    void Foo()
    {
        Expression<Func<string>> baseString = () => base.ToString();
    }

    public override string ToString()
    {
        return "overridden value";
    }
}

Here that would represent a non-virtual call to Object.ToString() (for this). I can't see how that would be represented in an expression tree, hence the error.

Now that leads on to the obvious question of why there isn't a representation of non-virtual base member invocation in expression trees - I'm afraid I can't answer that part... although I can see that if you could build that expression programmatically, that would allow you to bypass normal polymorphism from the outside instead of only from inside the class itself (which is the normal case). That may be the reason. (Admittedly there are other ways of calling methods non-virtually, but that's a different matter, and I dare say there are situations where expression trees are "trusted" but other code isn't.)



回答2:

Jon's answer is correct. I want to follow up on Jon's comment:

I can see that if you could build that expression programmatically, that would allow you to bypass normal polymorphism from the outside instead of only from inside the class itself (which is the normal case). That may be the reason

Suppose you have

public abstract class B // Prevent instantiation
{
    internal B() {} // Prevent subclassing outside the assembly. 
    public virtual void Dangerous() { ... } 
}
public sealed class D : B 
{ 
  public override void Dangerous() 
  { 
    if (!Allowed()) throw whatever;
    base.Dangerous();
  }

There should be no way for partially trusted code with a D in hand to call B.Dangerous on the instance of D without doing the safety check in D.Dangerous.

The CLR verifier therefore restricts you from performing a non-virtual invocation (a base invocation is of course non-virtual) on a virtual method from outside the class hierarchy. In fact, it goes even farther; you can't even perform that from a class nested within D ! (Of course if your program is granted the right to skip verification then you can do whatever you want; you can dereference arbitrary pointers to memory in unverifiable code which is a lot worse than making a static call on a virtual method.)

When we were designing expression trees we didn't want to deal with this messy security problem. The easiest thing to do was to simply make the whole thing illegal.

There were a number of other security problems with expression trees that could not so easily be solved, but those are a topic for another day.