Какова цель 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 обеспечивает независимый контроль над именами, которые видны из базового типа (скрытие), и совместное использование слотов компоновки в производном классе (переопределение). Скрытие контролируется путем пометки члена в производном классе как скрытия по имени или скрытия по имени и подписи. Скрытие всегда выполняется в зависимости от типа члена, то есть производные имена полей могут скрывать базовые имена полей, но не имена методов, имена свойств или имена событий. Если производный член помечен как скрытый по имени, то члены того же вида в базовом классе с тем же именем не будут видны в производном классе; если член помечен как скрытый именем и сигнатурой, то из производного класса будет скрыт только член того же вида с точно таким же именем и типом (для полей) или сигнатурой метода (для методов). Реализация различия между этими двумя формами сокрытия полностью обеспечивается компиляторами исходного языка и библиотекой отражений; это не имеет прямого влияния на саму ВЭС.
(Это не сразу понятно из этого, но 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
, хотя вы все равно можете привести его на базу и назвать так.
РЕДАКТИРОВАТЬ: я только что попробовал это, скомпилировав код выше в 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();
(Нет проблем компиляции.)
Согласно ответу THE SKEET, кроме того, причина в том, что Java и C# позволяют клиенту класса вызывать любые методы с одинаковыми именами, в том числе из базовых классов. В то время как C++ этого не делает: если производный класс определяет даже один метод с тем же именем, что и метод в базовом классе, то клиент не может напрямую вызывать метод базового класса, даже если он не принимает те же аргументы. Поэтому функция была включена в CIL для поддержки обоих подходов к перегрузке.
В C++ вы можете эффективно импортировать один именованный набор перегрузок из базового класса с помощью using
директива, чтобы они стали частью "набора перегрузки" для этого имени метода.
Согласно Microsoft Docs
Когда член в производном классе объявлен с C#
new
модификатор или Visual BasicShadows
Модификатор, он может скрыть член с тем же именем в базовом классе. C# скрывает членов базового класса по подписи. То есть, если член базового класса имеет несколько перегрузок, единственная, которая скрыта, - это та, которая имеет идентичную подпись. В отличие от Visual Basic скрывает все перегрузки базового класса. Таким образом, IsHideBySig возвращаетfalse
на члене, объявленном с помощью Visual BasicShadows
модификатор иtrue
на член объявлен с C#new
модификатор.