Что нужно помнить, так это то, что динамическое разрешение в основном выполняет тот же процесс, что и статическое разрешение, но во время выполнения. Все, что не может быть разрешено 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
имеет слот для магазина, поэтому он используется для actualTest.Store
. IExtendedInterface
нет, поэтому для звонка используется IActualInterface
. TestInterface
определяет новый метод Store, используя модификатор IL newslot
, эффективно назначая новый слот в таблице 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# выбрать его для нас. Я изменил ПЯ выглядеть следующим образом:
Для интерфейсов:
// 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 и аварий во время выполнения со следующей ошибкой:
Unhandled Exception: System.MissingMethodException: Method not found: 'Void ConsoleApplication38.IExtendedInterface.Store(System.Object)'. at ConsoleApplication38.Program.TestInterfaces() at ConsoleApplication38.Program.Main(String[] args)
Если удалить этот недопустимый вызов, производные классы вызовов работают отлично без каких-либо ошибок. CLR может разрешить базовый метод из вызова производного типа.Однако интерфейсы не имеют истинного представления во время выполнения, и CLR не может разрешить вызов метода из расширенного интерфейса.
Теоретически компилятор C# может испускать вызов непосредственно в класс, указанный в среде выполнения. Это позволит избежать проблем с вызовами средних классов, как показано на Eric Lippert's blog. Однако, как показано, это невозможно для интерфейсов.
Вернемся к DLR. Он решает метод точно так же, как и CLR. Мы видели, что IExtendedInterface.Store
не может быть разрешен средой CLR. DLR тоже не может! Это полностью скрывается из-за того, что компилятор C# будет выдавать правильный вызов, поэтому всегда будьте осторожны при использовании dynamic
, если вы не знаете, как он работает в среде CLR.
Нашли это, работая на Ворон, не так ли? –
@Chris конечно сделал !! –