Какова цель hidebysig в ​​методе MSIL?

Использование ildasm и программы С#, например

static void Main(string[] args)
{

}

дает:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method Program::Main

Что делает конструкция hidebysig?

Ответ 1

Из ECMA 335, раздел 8.10.4 раздела 1:

CTS обеспечивает независимый контроль над видимыми именами от базового типа (скрыть) и совместное использование слотов макета в класс (переопределение). Скрытие контролируется путем маркировки члена в производный класс как скрытый по имени или скрыть по имени и подписи. Прячется всегда выполняется на основе вида члена, т.е. производного поля имена могут скрывать имена базового поля, но не имена методов, имена свойств или имена событий. Если производный член отмечены скрыть по имени, затем члены тот же вид в базовом классе с одно и то же имя не отображается в производный класс; если элемент отмечен скрыть по имени и подписи, тогда только член того же типа с точно одно и то же имя и тип (для полей) или подпись метода (для методов) скрытый от производного класса. Выполнение различия между этими двумя формами предоставляемый полностью исходным языком компиляторы и библиотека отражения; он не оказывает прямого воздействия на VES сам по себе.

(Это не сразу очищается от этого, но hidebysig означает "скрыть по имени и подписи".)

Также в разделе 15.4.2.2 раздела 2:

hidebysig предоставляется для использования инструментов и игнорируется VES. Это указывает, что объявленный метод скрывает все методы базового класса типы, которые имеют метод сопоставления подпись; при опускании метод должен скрывать все методы одного и того же имя, независимо от подписи.

В качестве примера предположим, что у вас есть:

public class Base
{
    public void Bar()
    {
    }
}

public class Derived : Base
{
    public void Bar(string x)
    {
    }
}

...

Derived d = new Derived();
d.Bar();

Это верно, потому что Bar(string) не скрывает Bar(), потому что компилятор С# использует hidebysig. Если бы он использовал семантику "спрятать по имени", вы бы вообще не смогли вызвать Bar() на ссылку типа Derived, хотя вы все равно могли бы передать ее в базу и вызвать ее таким образом.

EDIT: Я только что попробовал это, скомпилировав вышеуказанный код в DLL, ildasming его, удалив hidebysig для Bar() и Bar(string), снова нажав его, а затем попытавшись вызвать Bar() из другого кода

Derived d = new Derived();
d.Bar();

Test.cs(6,9): error CS1501: No overload for method 'Bar' takes '0' arguments

Однако:

Base d = new Derived();
d.Bar();

(Нет проблем с компиляцией.)

Ответ 2

В соответствии с ответом SKEET, кроме того, причина в том, что Java и С# позволяют клиенту класса вызывать любые методы с тем же именем, в том числе и базовые классы. В то время как С++ не делает этого: если производный класс определяет даже один метод с тем же именем, что и метод в базовом классе, тогда клиент не может напрямую вызвать метод базового класса, даже если он не принимает те же аргументы. Таким образом, функция была включена в CIL для поддержки обоих подходов к перегрузке.

В С++ вы можете эффективно импортировать один и тот же набор перегрузок из базового класса с директивой using, чтобы они стали частью "набора перегрузки" для этого имени метода.