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

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

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?

3 ответов


С ECMA 335 раздел 8.10.4 раздела 1:

CTS обеспечивает независимый контроль над обоими именами, которые видны от базового типа (скрытие) и совместное использование слотов макета в производном класс (переопределение). Сокрытие контролируемый путем маркировать член в производный класс как hide by name или скрыть по имени и подписи. Прячется всегда выполняется на основе вида элемента, то есть производного поля имена могут скрыть имена полей, но не имена методов, имен свойств или имя события. Если производный элемент помечено скрыть по имени, затем члены то же самое в базовом классе с одно и то же имя не отображается в производный класс; если элемент отмечен скрыть имя и подпись только член же с точно то же имя и тип (для полей) или сигнатура метода (для методов) является скрыто от производного класса. Проведение различия между эти две формы сокрытия полностью на языке оригинала компиляторы и библиотека отражений; это не имеет прямого влияния на 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(), потому что компилятор C# использует hidebysig. Если бы он использовал семантику "скрыть по имени", вы не смогли бы вызвать Bar() вообще по ссылке типа Derived, хотя вы все еще может бросить его на базу и назвать его так.

EDIT: я только что попробовал это, скомпилировав вышеуказанный код в DLL, ildasming его, удалив hidebysig на Bar() и Bar(string), ilasming он снова пытался позвонить 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();

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


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

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


по данным Microsoft Docs

когда член в производном классе объявляется с C# new модификатор или Visual Basic Shadows модификатор, он может скрыть член того же имя в базовом классе. C# скрывает члены базового класса по подписи. Что есть если член базового класса имеет несколько перегрузок, единственное, что скрыт тот, который имеет идентичную подпись. По контрасту, Visual Basic скрывает все перегрузки базового класса. Таким образом, IsHideBySig возвращает false на члене, объявленном с помощью Visual Basic Shadows модификатор, и true на члене, объявленном с помощью C# new модификатор.