Что означает термин затенение в C #? Я прочитал эту ссылку, но не совсем понял ее.
Что такое слежка?
Ответы (11)
Затенение скрывает метод в базовом классе. Используя пример в вопросе, который вы связали:
class A
{
public int Foo(){ return 5;}
public virtual int Bar(){return 5;}
}
class B : A
{
public new int Foo() { return 1;}
public override int Bar() {return 1;}
}
Класс B
переопределяет виртуальный метод Bar
. Он скрывает (затеняет) невиртуальный метод Foo
. При переопределении используется ключевое слово переопределить. Затенение выполняется с помощью ключевого слова новое.
В приведенном выше коде, если вы не использовали ключевое слово new при определении метода Foo
в классе B
, вы получите это предупреждение компилятора:
'test.B.Foo()' hides inherited member 'test.A.Foo()'. Use the new keyword if hiding was intended.
- Переопределение: переопределение существующего метода в базовом классе
- Затенение: создание совершенно нового метода с той же сигнатурой, что и в базовом классе
Предположим, у меня есть базовый класс, реализующий виртуальный метод:
public class A
{
public virtual void M() { Console.WriteLine("In A.M()."); }
}
У меня также есть производный класс, который также определяет метод M:
public class B : A
{
// could be either "new" or "override", "new" is default
public void M() { Console.WriteLine("In B.M()."); }
}
Теперь предположим, что я напишу такую программу:
A alpha = new B(); // it's really a B but I cast it to an A
alpha.M();
У меня есть два разных варианта реализации. Поведение по умолчанию - вызвать версию M. (Это идентично поведению, если вы применили ключевое слово "new
" к B.M()
.)
Это называется «затенением», когда у нас есть метод с тем же именем, но с другим поведением при вызове из базового класса.
В качестве альтернативы мы могли указать "override
" на B.M()
. В этом случае alpha.M()
назвал бы версию М.
Затенение заключается в сокрытии метода базового класса с новым определением в дочернем классе.
Разница между скрытием и переопределением связана со способом вызова методов.
Таким образом, когда виртуальный метод переопределяется, адрес вызова таблицы вызовов методов базового класса заменяется адресом дочерней процедуры.
С другой стороны, когда метод скрыт, новый адрес добавляется в таблицу вызовов методов дочернего класса.
Когда вызывается рассматриваемый метод:
- Получается тип класса таблицы вызова методов, если мы вызываем со ссылкой на базовый класс, тогда получается таблица методов базового класса, если у нас есть ссылка на дочерний класс, тогда получается таблица методов дочернего класса.
- Метод ищется в таблице, если он найден, то выполняется вызов, в противном случае ищется таблица методов базового класса.
Если мы вызываем метод со ссылкой на дочерний класс, то поведение такое же, если метод был переопределен, адрес метода будет найден в базовом классе, если метод был скрыт, адрес метода будет найден в дочерний класс, и, поскольку он уже найден, поиск в таблице базового класса выполняться не будет.
Если мы вызываем метод со ссылкой на базовый класс, поведение меняется. При переопределении, поскольку адрес метода перезаписывает запись базового класса, мы будем вызывать дочерний метод, даже если он содержит ссылку на базовый класс. При затенении таблица методов базового класса (которая является единственной видимой, поскольку мы храним ссылку на базовый класс) содержит адрес виртуального метода, и поэтому будет вызван метод базового класса.
В общем, затенение - плохая идея, так как оно вносит различие в поведение экземпляра в зависимости от ссылки на него.
Расширяя правильный ответ Кента
При устранении неоднозначности, когда будет вызван метод, мне нравится думать о затенении и переопределении с помощью следующих
- Затенение: вызываемый метод зависит от типа ссылки в точке вызова.
- Переопределение: вызываемый метод зависит от типа объекта в точке вызова.
Вот статья MSDN о теневом использовании. Примеры языков представлены на Visual Basic (к сожалению, в MSDN нет эквивалентной страницы C #), но в целом он имеет дело с концепциями и, надеюсь, все равно поможет вам понять.
Изменить: похоже, что есть статья на C # о теневом копировании, за исключением того, что в C # это называется скрытием. Кроме того, эта страница предлагает хороший обзор.
Если вы хотите скрыть метод базового класса, используйте переопределение в базе [виртуальный метод в базе]
если вы хотите скрыть метод дочернего класса, используйте новый в базе [невиртуальный метод в базе] -> тень
Base B=new Child()
B.VirtualMethod()
-> Вызов метода дочернего класса
B.NonVirtualMethod()
-> Вызов метода базового класса
Переопределение: одно и то же имя и одни и те же параметры, по-разному реализованные в подклассах.
- Если он рассматривается как DerivedClass или BaseClass, он использует производный метод.
Затенение: одно и то же имя и точно такие же параметры, по-разному реализованные в подклассах.
- При обработке как DerivedClass используется производный метод.
- если рассматривается как BaseClass, он использует базовый метод.
Надеюсь, это краткое объяснение поможет. Shadowing
- Заменяет весь элемент родительского класса
class InventoryAndSales
{
public int InvoiceNumber { get; set; }
}
//if someone calls for this class then the InvoiceNumber type is now object
class NewInventoryAndSales : InventoryAndSales
{
public new object InvoiceNumber { get; set; }
}
Overriding
- заменяет только реализацию. Он не заменяет тип данных, который он не заменяет, например, у вас есть переменная, она не преобразует ее в метод, поэтому, если есть метод, он будет использовать этот метод и изменит только реализацию
class InventoryAndSales
{
public virtual int GetTotalSales(int a, int b)
{
return a + b;
}
}
class NewInventoryAndSales : InventoryAndSales
{
//it replaces the implementation in parent class
public override int GetTotalSales(int a, int b)
{
return a * b;
}
}
Затенение - это не то, о чем я бы беспокоился о понимании или реализации, если оно не "соответствует" проблеме действительно хорошо. Я видел, как он используется неправильно и вызывает странные логические ошибки гораздо чаще, чем при правильном использовании. Я думаю, что основная причина заключается в том, что когда программист забывает указать переопределения в сигнатуре метода, предупреждение компилятора предложит новое ключевое слово. Я всегда считал, что вместо этого следует рекомендовать использовать переопределение.
private static int x = 10;
static void Main(string[] args)
{ int x = 20;
if (Program.x == 10)
{
Console.WriteLine(Program.x);
}
Console.WriteLine(x);}
Выход:
10 20