Метод скрывается в С# с допустимым примером. почему он реализован в рамках? Каково преимущество реального мира?

Может ли кто-нибудь объяснить фактическое использование метода скрытия в С# с допустимым примером?

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

Есть ли какая-либо конкретная причина использовать ключевое слово new?

Ответ 1

С# не только поддерживает переопределение метода, но и метод скрывает. Проще говоря, если метод не переопределяет производный метод, он скрывает его. Метод скрытия должен быть объявлен с использованием нового ключевого слова. Правильное определение класса во втором листинге:

    using System;
    namespace Polymorphism
    {
        class A
        {
            public void Foo() { Console.WriteLine("A::Foo()"); }
        }

        class B : A
        {
            public new void Foo() { Console.WriteLine("B::Foo()"); }
        }

        class Test
        {
            static void Main(string[] args)
            {
                A a;
                B b;

                a = new A();
                b = new B();
                a.Foo();  // output --> "A::Foo()"
                b.Foo();  // output --> "B::Foo()"

                a = new B();
                a.Foo();  // output --> "A::Foo()"
            }
        }
    }

Ответ 2

Одно из применений, которое я иногда использую для нового ключевого слова, относится к ковариации "плохой мужской собственности" в дереве наследования параллелизма. Рассмотрим этот пример:

public interface IDependency
{
}

public interface ConcreteDependency1 : IDependency
{
}

public class Base
{
  protected Base(IDependency dependency)
  {
    MyDependency = dependency;
  }

  protected IDependency MyDependency {get; private set;}
}

public class Derived1 : Base // Derived1 depends on ConcreteDependency1
{
  public Derived1(ConcreteDependency1 dependency)  : base(dependency) {}

  // the new keyword allows to define a property in the derived class
  // that casts the base type to the correct concrete type
  private new ConcreteDependency1 MyDependency {get {return (ConcreteDependency1)base.MyDependency;}}
}

Дерево наследования Derived1: Base имеет "параллельную зависимость" от ConcreteDependency1: IDependency. В производном классе я знаю, что MyDependency имеет тип ConcreteDependency1, поэтому я могу скрыть свойство getter из базового класса с помощью нового ключевого слова.

EDIT: см. также это сообщение в блоге Эрика Липперта для хорошего объяснения нового ключевого слова.

Ответ 3

Я думаю, что пример ArsenMkrt не совсем корректен, по крайней мере, он не полностью объясняет функцию укрытия. Отбрасывая новое ключевое слово из метода Foo в классе B, вы все равно получите вывод

A::Foo()
B::Foo()
A::Foo()

На языке программирования, таком как Java, где все методы являются "виртуальными", вы ожидаете получить выходные данные

A::Foo()
B::Foo()
B::Foo()

взяв код ArsenMkrt выше, из-за экземпляра

A a;
B b;

a = new A();
b = new B();
a.Foo(); 
b.Foo(); 

a = new B(); //<< Here
a.Foo();  

В своем примере, однако, вы все равно получаете "A:: Foo()", потому что по методам С# по умолчанию не являются виртуальными, поэтому метод B:: Foo() автоматически скрывает A Foo(). Чтобы добиться полиморфного поведения, нужно записать его следующим образом:

class A
{
    public virtual void Foo() { Console.WriteLine("A::Foo()"); }
}

class B : A
{
    public override void Foo() { Console.WriteLine("B::Foo()"); }
}

В настоящее время появляется ключевое слово "новое". Фактически, когда вы покидаете "переопределение" из B:: Foo(), вы снова скроете A:: Foo(), что означает, что вы не переопределяете его по умолчанию поведение, и вы не достигаете полиморфизма, т.е. вы снова получаете "A:: Foo()" в качестве вывода. То же самое может быть достигнуто - и здесь, где я не на 100% понимаю, зачем вам это нужно, поставив "новое" ключевое слово, как..

class A
{
    public virtual void Foo() { Console.WriteLine("A::Foo()"); }
}

class B : A
{
    public new void Foo() { Console.WriteLine("B::Foo()"); }
}

и вы снова получите вывод

A::Foo()
B::Foo()
A::Foo()

Ответ 4

Один слегка скрытый сценарий, в котором сокрытие метода было бы уместным, но из-за отсутствия какого-либо чистого идиоматического способа его выражения в обстоятельствах, когда базовый класс предоставляет защищенный член наследуемому потомку, но этот потомок знает, что существует так как никакие последующие производные классы не могли использовать этот член, не нарушая его. Ярким примером метода, который многие классы должны скрывать (но очень мало), является MemberwiseClone(). Во многих случаях, если класс имеет член private, который ожидает, что он будет единственной ссылкой на изменчивый объект, нет возможных способов, с помощью которых производный класс мог бы правильно использовать MemberwiseClone().

Обратите внимание, что скрытие защищенных членов не является нарушением LSP. Соответствие с LSP требует, чтобы в тех местах, где код мог ожидать ссылки на объект базового класса, но получает ссылку на объект производного класса, последний объект должен работать так же, как и базовый класс. Защищенные члены, однако, выходят за рамки LSP, так как каждый тип абсолютно знает свой базовый тип. Если Bar и Boz оба выводятся из Foo, а Boz скрывает защищенный член, который требуется Bar, тот факт, что Boz скрывает элемент, не повлияет на Bar, потому что Bar экземпляр базового типа не может быть Boz или ничего, кроме Foo. Никакая замена не возможна, и, следовательно, замещаемость не имеет значения.

Ответ 5

Это может помочь.

class Base
{
   public void F() {}
}
class Derived: Base
{
   public void F() {}      // Warning, hiding an inherited name
}

В приведенном выше примере объявление F в Derived приводит к сообщению о предупреждении. Скрытие унаследованного имени, в частности, не является ошибкой, поскольку это исключает отдельную эволюцию базовых классов. Например, вышеупомянутая ситуация могла возникнуть, потому что более поздняя версия Base вводила метод F, который отсутствовал в более ранней версии класса. Если бы вышеупомянутая ситуация была ошибкой, любое изменение, внесенное в базовый класс в библиотеке классов с отдельным версией, потенциально может привести к тому, что производные классы станут недействительными. Источник: https://msdn.microsoft.com/en-us/library/aa691135%28v=vs.71%29.aspx

Ответ 6

Одно время, когда вы будете использовать их, - это когда вам нужно добавить/изменить атрибут, основанный на методе базового класса. Например:

[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new event EventHandler TextChanged
{
    add { base.TextChanged += value; }
    remove { base.TextChanged -= value; }
}

В базовом классе Control есть событие TextChanged, но базовое событие имеет всевозможные атрибуты, наложенные на него, чтобы предотвратить его появление в intellisense или дизайнере. Поскольку класс я использовал это в широко используемом как свойство Text, так и событие TextChanged, я хотел, чтобы событие TextChanged отображалось в intellisense и было видимым в окне свойств.