Необходимо помнить, что динамическое разрешение в основном выполняет те же действия, что и статическое, но во время выполнения.Все, что не может быть разрешено CLR, не будет разрешено DLR.
Давайте возьмем эту маленькую программу, вдохновленную вашей, и которая вообще не использует динамический:
namespace ConsoleApplication38 {
public interface IActualInterface {
void Store(object entity);
}
public interface IExtendedInterface : IActualInterface {
}
public class TestInterface : IExtendedInterface {
public void Store(object entity) {
}
}
public abstract class ActualClass {
public abstract void Store(object entity);
}
public abstract class ExtendedClass : ActualClass {
}
public class TestClass : ExtendedClass {
public override void Store(object entity) {
}
}
class Program {
static void TestInterfaces() {
IActualInterface actualTest = new TestInterface();
IExtendedInterface extendedTest = new TestInterface();
TestInterface directTest = new TestInterface();
actualTest.Store(null);
extendedTest.Store(null);
directTest.Store(null);
}
static void TestClasses() {
ActualClass actualTest = new TestClass();
ExtendedClass extendedTest = new TestClass();
TestClass directTest = new TestClass();
actualTest.Store(null);
extendedTest.Store(null);
directTest.Store(null);
}
static void Main(string[] args) {
TestInterfaces();
TestClasses();
}
}
}
Все компилируется нормально.Но что на самом деле сгенерировал компилятор?Давайте рассмотрим использование ILdasm.
Для интерфейсов:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.IActualInterface::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.IActualInterface::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.TestInterface::Store(object)
Здесь мы видим, что компилятор C # всегда генерирует вызовы для интерфейса или класса, в котором определен метод.IActualInterface
имеет слот для метода Store, поэтому он используется для actualTest.Store
.IExtendedInterface
нет, поэтому для звонка используется IActualInterface
.TestInterface
определяет новый метод Store, используя модификатор newslot
IL, эффективно назначая новый слот в vtable для этого метода, поэтому он используется напрямую, поскольку directTest
имеет тип TestInterface
.
Для классов:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
Для 3 различных типов генерируется один и тот же вызов, поскольку слот метода определен в ActualClass.
Давайте теперь посмотрим, что мы получим, если напишем ILсами, используя тот тип, который мы хотим, вместо того, чтобы позволить компилятору C # выбрать его для насЯ изменил IL, чтобы он выглядел так:
Для интерфейсов:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.IActualInterface::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.IExtendedInterface::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.TestInterface::Store(object)
Для классов:
// actualTest.Store
IL_0015: callvirt instance void ConsoleApplication38.ActualClass::Store(object)
// extendedTest.Store
IL_001d: callvirt instance void ConsoleApplication38.ExtendedClass::Store(object)
// directTest.Store
IL_0025: callvirt instance void ConsoleApplication38.TestClass::Store(object)
Программа прекрасно компилируется с ILasm.Тем не менее, он не проходит peverify и падает во время выполнения со следующей ошибкой:
Необработанное исключение: System.MissingMethodException: Метод не найден: 'Void ConsoleApplication38.IExtendedInterface.Store (System.Object)'.в ConsoleApplication38.Program.TestInterfaces () в ConsoleApplication38.Program.Main (String [] args)
Если вы удалите этот недопустимый вызов, вызовы производных классов будут работать без ошибок.CLR может разрешить базовый метод из вызова производного типа.Однако интерфейсы не имеют истинного представления во время выполнения, и CLR не может разрешить вызов метода из расширенного интерфейса.
Теоретически, компилятор C # может отправлять вызов непосредственно в правильный класс, указанный вво время выполнения.Это позволило бы избежать проблем с вызовами среднего класса, как видно из в блоге Эрика Липперта .Однако, как показано, это невозможно для интерфейсов.
Вернемся к DLR.Он разрешает метод точно так же, как и CLR.Мы видели, что CLR не может разрешить IExtendedInterface.Store
.DLR тоже не может!Это полностью скрыто тем фактом, что компилятор C # отправит правильный вызов, поэтому всегда будьте осторожны при использовании dynamic
, если вы не знаете, как он работает в CLR.