Точная разница между перекрытием и скрытием

Может ли кто-нибудь сказать, как работает переопределение и скрытие с точки зрения памяти и ссылок.

class A
{
    public virtual void Test1() { //Impl 1}
    public virtual void Test2() { //Impl 2}
}
class B  : A
{
    public override void Test1() { //Impl 3}
    public new void Test2() { Impl 4}
}

static Main()
{
    A aa=new B() //This will give memory to B
    aa.Test1(); //What happens in terms of memory when this executes
    aa.Test2(); //-----------------------SAME------------------------
}

Здесь память относится к классу B, но во втором операторе aa.Test2 будет вызван метод класса A. Почему это? Если у B есть память, то должен быть вызван метод B (с моей точки зрения).

Любая ссылка / упражнение, которое очень глубоко и полно описывает этот фундамент, будет большим подспорьем.


person Akki J    schedule 15.06.2012    source источник
comment
Также может кто-нибудь сказать мне вариант использования скрытия метода?   -  person Akki J    schedule 15.06.2012
comment
Поскольку aa - это A, а не B. Попробуйте с ((B) aa) .Test2 ()   -  person Tisho    schedule 15.06.2012
comment
@Tisho Правильно, но память с B, а не с A?   -  person Akki J    schedule 15.06.2012
comment
Вы должны явно привести к B, чтобы получить доступ к методам B. aa по-прежнему является A.   -  person Tisho    schedule 15.06.2012


Ответы (9)


Взгляните на этот ответ на другой вопрос Эрика Липперта.

Перефразируя (в пределах моего понимания), эти методы входят в «слоты». A имеет два слота: один для Test1 и один для Test2.

Поскольку A.Test1 обозначен как virtual, а B.Test1 обозначен как override, реализация Test1 в B не создает свой собственный слот, а перезаписывает реализацию A. Независимо от того, обрабатываете ли вы экземпляр B как B или приводите его к A, одна и та же реализация находится в этом слоте, поэтому вы всегда получаете результат B.Test1.

Напротив, поскольку B.Test2 помечен как new, он создает свой собственный новый слот. (Как если бы он не был помечен new, но получил другое имя.) Реализация Test2 A все еще «там» в своем собственном слоте; он был скрыт, а не перезаписан. Если рассматривать экземпляр B как B, вы получите B.Test2; если вы приведете его к A, вы не сможете увидеть новый слот, и будет вызван A.Test2.

person Rawling    schedule 15.06.2012
comment
Это отличный способ понять концепцию полиморфизма. Однако я не знаю, действительно ли эта штука прорезания слотов происходит в памяти или нет. Большое спасибо, Роулинг .... - person Akki J; 15.06.2012
comment
@AkkiJ Я мог бы представить, что это буквальные элементы где-то в памяти, что-то вроде _1 _ / _ 2_s, но я не могу сказать наверняка. - person Rawling; 15.06.2012
comment
@AkkiJ: виртуальные таблицы существуют один раз для каждого подтипа (класса), они не создаются каждый раз, когда создается экземпляр объекта. Если ваш базовый класс содержит виртуальный метод, каждый экземпляр этого класса будет содержать дескриптор типа (указатель на его виртуальную таблицу) и (если таковые существуют) поля экземпляра. Если в вашем классе нет полей, его экземпляр всегда будет содержать ровно 12 байтов. - person Groo; 15.06.2012

Чтобы добавить в ответ @ Rawling, можно показать практические примеры, используя такой пример:

class Base
{
    // base property
    public virtual string Name
    {
        get { return "Base"; }
    }
}

class Overriden : Base
{
    // overriden property
    public override string Name
    {
        get { return "Overriden"; }
    }
}

class New : Base
{
    // new property, hides the base property
    public new string Name
    {
        get { return "New"; }
    }
}

1. Переопределение

В случае свойства overriden слот виртуального метода базового класса заменяется другой реализацией. Компилятор видит метод как виртуальный и должен разрешить его реализацию во время выполнения, используя виртуальную таблицу объекта.

{
    Base b = new Base();
    Console.WriteLine(b.Name); // prints "Base"

    b = new Overriden();
    // Base.Name is virtual, so the vtable determines its implementation
    Console.WriteLine(b.Name); // prints "Overriden"

    Overriden o = new Overriden();
    // Overriden.Name is virtual, so the vtable determines its implementation
    Console.WriteLine(o.Name); // prints "Overriden"
}

2. Скрытие

Когда метод или свойство скрыто с помощью ключевого слова new, компилятор создает новый невиртуальный метод только для производного класса; метод базового класса остается нетронутым.

Если тип переменной Base (т.е. содержит только виртуальный метод), его реализация будет разрешена через vtable. Если тип переменной New, то будет вызван не виртуальный метод или свойство.

{
    Base b = new Base();
    Console.WriteLine(b.Name); // prints "Base"

    b = new New();
    // type of `b` variable is `Base`, and `Base.Name` is virtual,
    // so compiler resolves its implementation through the virtual table
    Console.WriteLine(b.Name); // prints "Base"

    New n = new New();
    // type of `n` variable is `New`, and `New.Name` is not virtual,
    // so compiler sees `n.Name` as a completely different property
    Console.WriteLine(n.Name); // prints "New"
}

3. Резюме

Если часть вашего кода принимает базовый тип, она всегда будет использовать виртуальную таблицу во время выполнения. Для большинства сценариев ООП это означает, что пометка метода как new очень похожа на присвоение ему совершенно другого имени.

4. Размеры объектов после создания

Обратите внимание, что создание любого из этих типов не создает копию виртуальной таблицы. Каждый .NET-объект имеет пару байтов заголовка и указатель на виртуальную таблицу таблицы своего типа (class).

Что касается свойства new (того, которое не является виртуальным), оно в основном компилируется как статический метод с семантикой thiscall, что означает, что он также ничего не добавляет к размеру экземпляра в памяти.

person Groo    schedule 15.06.2012
comment
Если вас больше интересует внутренняя память .NET, чем поведение компилятора, проверьте эту замечательную ссылку: . Внутренние элементы .NET Framework: как среда CLR создает объекты времени выполнения, и в данном случае Экземпляр объекта и Таблица методов главы. Это может показаться длинным чтением, но каждый .NET-программист должен прочитать его в какой-то момент. - person Groo; 15.06.2012
comment
У меня есть все время и интерес, чтобы читать подробные обсуждения реальной работы .NET. Большое спасибо за отличное объяснение и важные ссылки. - person Akki J; 15.06.2012

Уже ответил на здесь

Переопределение - это определение нескольких возможных реализаций одной и той же сигнатуры метода, так что реализация определяется типом среды выполнения нулевого аргумента (обычно идентифицируемого именем this в C #).

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

Практическая разница между переопределением и скрытием заключается в следующем:

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

• Если метод переопределен, реализация для вызова основана на типе времени выполнения аргумента this. • Если метод просто скрыт, вызываемая реализация основана на типе аргумента this во время компиляции.

Вот несколько примеров: Пример №1. и Пример №2

person Ebad Masood    schedule 15.06.2012

Метод Test1 () в классе A и метод test1 () в классе B будут выполняться в соответствии с MethdOverriding.

Метод Test2 () в классе A и метод test2 () в классе B будут выполняться в соответствии с Скрытием метода.

В методе Переопределение будут выполняться члены дочернего класса, а в методе Скрытие метода будут выполняться члены родительского класса.

person Krish    schedule 11.07.2014

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

// base
    public int GrossAmount { get; set; }

    // hiding base
    public new string GrossAmount
    {
        get;
        set;             
    }
person Johnv2020    schedule 15.06.2012

Если вычесть из предоставленного кода, у вас должно получиться B:A.

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

По моему опыту, я использовал сокрытие в основном для debug целей.

Например, когда я не знаю, кто устанавливает свойство какого-то 3-го prt component, код которого мне недоступен. Итак, что я делаю:

  • создать дочерний класс из компонента
  • скрыть интересующее свойство с помощью ключевого слова new
  • поставить точку останова в set
  • и ждите когда попадут.

Иногда это очень полезно и помогает мне быстро получить информацию, особенно на первом этапе, когда вы изучаете новые components, frameworks, libraries .. что угодно.

person Tigran    schedule 15.06.2012

Скрывая метод или свойство, вы просто заявляете, что хотите, чтобы такой метод был полиморфным, если у вас есть объект этого типа. Кроме того, скрытые методы вызываются неполиморфным способом, поэтому для вызова этого типа метода необходимо знать во время компиляции, поскольку это был просто не виртуальный метод.

person Felice Pollano    schedule 15.06.2012

 public class BaseClass
    {
      public void PrintMethod()
      {
       Console.WriteLine("Calling base class method");
      }
     }
     public class ChildClass
     {
      public new void PrintMethod()
      {
       Console.WriteLine("Calling the child or derived class method");
       }
      }
      class Program
      {
       static void Main()
       {
        BaseClass bc = new ChildClass();
        bc.PrintMethod();
        }
       }

Метод скрытия заключается в том, что когда ссылочная переменная базового класса указывает на объект дочернего класса. Он вызовет скрытый метод в базовом классе.

Где как, Когда мы объявляем виртуальный метод в базовом классе. Мы переопределяем этот метод в производном или дочернем классе. Тогда ссылочная переменная базового класса вызовет метод производного класса. Это называется переопределением метода.

person Gagan    schedule 07.05.2015

class Base {
    int a;
    public void Addition() {
        Console.WriteLine("Addition Base");
    }
    public virtual void Multiply()
    {
        Console.WriteLine("Multiply Base");
    }
    public void Divide() {
        Console.WriteLine("Divide Base");
    }
}

class Child : Base
{
    new public void Addition()
    {
        Console.WriteLine("Addition Child");
    }
    public override void Multiply()
    {
        Console.WriteLine("Multiply Child");
    }
    new public void Divide()
    {
        Console.WriteLine("Divide Child");
    }
}
class Program
{        
    static void Main(string[] args)
    {
        Child c = new Child();
        c.Addition();
        c.Multiply();
        c.Divide();

        Base b = new Child();
        b.Addition();
        b.Multiply();
        b.Divide();

        b = new Base();
        b.Addition();
        b.Multiply();
        b.Divide();
    }
}

Вывод : -

Дополнение Child

Умножить ребенка

Разделить ребенка

База дополнений

Умножить ребенка

Разделить базу

База дополнений

Умножить базу

Разделить базу

Во время переопределения компилятор проверяет объект класса, но при скрытии компилятор проверяет только ссылку на класс

person Shubham Sharma    schedule 28.07.2017