Внутренний статический внешний объект С# с атрибутом InternalCall - внутренний или внешний?

В еще один вопрос, который я задал, появился комментарий, указывающий, что метод .NET framework Array.Copy использует неуправляемый код. Я пошел копаться с Reflector и обнаружил, что подпись одной из перегрузок метода Array.Copy определяется так:

[MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);

Посмотрев на это, я немного смущен. Источником моей путаницы является модификатор extern, который означает (MSDN):

Модификатор extern используется для объявления метод, который реализуется извне.

Однако объявление метода также украшено атрибутом MethodImplOptions.InternalCall, который указывает (MSDN):

Задает внутренний вызов. внутренний вызов - это вызов метода который реализуется в рамках общей язык выполнения.

Может ли кто-нибудь объяснить это кажущееся очевидное противоречие?

Ответ 1

Я бы просто прокомментировал сообщение в leppie, но это было немного длиннее.

В настоящее время я работаю над экспериментальной реализацией CLI. Существует много случаев, когда публично открытый метод (или свойство) не может быть реализован без знания того, как виртуальная машина реализуется внутри страны. Одним из примеров является OffsetToStringData, который требует знания того, как диспетчер памяти выделяет строки.

В таких случаях, когда нет кода С# для выражения метода, вы можете рассматривать каждый вызов метода особым образом, внутренним для процесса JIT. В качестве примера, заменив код call байтом на ldc.i4 (целое число нагрузки), прежде чем передавать его в генератор нативного кода. Флаг InternalCall означает "Тело этого метода обрабатывается особым образом самой средой выполнения". Там может быть или не быть реальная реализация - в некоторых случаях в моем коде вызов JACK обрабатывается как intrinsic.

Существуют и другие случаи, когда JIT может иметь специальную информацию, которая позволяет значительно оптимизировать метод. Одним из примеров является метод Math, хотя даже эти могут быть реализованы на С#, указав InternalCall, чтобы сделать их эффективно intrinsics, имеет значительный производительности.

В С# метод должен иметь тело, если оно не является abstract или extern. extern означает общий "Вы можете вызывать этот метод из кода С#, но его тело фактически определено в другом месте". Когда JIT достигает вызова метода extern, он ищет, где найти тело и ведет себя по-разному в результате.

  • Атрибут DllImport указывает JIT сделать блокировку P/Invoke для вызова реализации собственного кода.
  • Флаг InternalCall указывает JIT обрабатывать вызов определенным образом.
  • (Есть и другие, но у меня нет примеров с моей головы для их использования.)

Ответ 2

InternalCall означает предоставленную инфраструктурой.

extern говорит, что вы не предоставляете код.

extern может использоваться в 2 общих ситуациях, например, выше, или с помощью p/invoke.

С помощью p/invoke вы просто укажете способ, где можно получить реализацию.