Ошибка в С#: "дерево выражений не может содержать базовый доступ" - почему бы и нет?
Я вызывал метод, который принимает Expression<Func<bool>>
.
Как часть выражения, которое я передавал:
this.Bottom == base.lineView.Top
Компилятор дал мне ошибку, что
дерево выражений не может содержать базовый доступ
Поэтому я просто изменил его на
this.Bottom == this.lineView.Top
потому что элемент был защищен в любом случае, и теперь он работает.
Но эта ошибка действительно вызвала меня: почему это проблема base
была проблемой? Особенно, если использование this
вместо этого будет работать, но синтаксически будет тем же результатом (к той же переменной обращаются)?
Ответы
Ответ 1
В документации System.Linq.Expressions.Expression
я не думаю, что существует тип выражения, который представляет "доступ к базовому члену". Не забывайте, что, хотя в вашем случае это означало то же самое, что только this
, в других случаях это не было бы:
class Test
{
void Foo()
{
Expression<Func<string>> baseString = () => base.ToString();
}
public override string ToString()
{
return "overridden value";
}
}
Здесь это будет представлять не виртуальный вызов Object.ToString()
(для this
). Я не вижу, как это будет представлено в дереве выражений, следовательно, ошибка.
Теперь это приводит к очевидному вопросу о том, почему нет представления о вызове не виртуального базового элемента в деревьях выражений - боюсь, я не могу ответить на эту часть... хотя я вижу, что если вы можете создать это выражение программно, что позволит вам обойти обычный полиморфизм извне, а не только внутри самого класса (что является нормальным случаем). Это может быть причиной. (По общему признанию, существуют другие способы вызова методов не виртуально, но это другой вопрос, и я смею утверждать, что существуют ситуации, когда деревья выражений "доверены", но другого кода нет.)
Ответ 2
Ответ Джона правильный. Я хочу следить за комментарием Джона:
Я вижу, что если бы вы могли программно создать это выражение, это позволило бы обойти нормальный полиморфизм извне, а не только внутри самого класса (что является нормальным случаем). Это может быть причиной
Предположим, что у вас есть
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();
}
Нельзя использовать частично доверенный код с D
для вызова B.Dangerous
в экземпляре D
без проверки безопасности в D.Dangerous
.
Таким образом, верификатор CLR запрещает вам выполнять не виртуальный вызов (базовый вызов, конечно, не виртуальный) по виртуальному методу извне иерархии классов. На самом деле, он идет еще дальше; вы даже не можете выполнить это из класса, вложенного в D
! (Конечно, если вашей программе предоставляется право пропустить проверку, то вы можете делать все, что хотите, вы можете разыменовывать произвольные указатели на память в непроверяемом коде, что намного хуже, чем статический вызов виртуального метода.)
Когда мы разрабатывали деревья выражений, мы не хотели иметь дело с этой грязной проблемой безопасности. Проще всего было просто сделать все незаконным.
Возникло множество других проблем безопасности с деревьями выражений, которые не могли быть легко решены, но это тема для другого дня.