Подтвердить что ты не робот

Есть ли когда-нибудь ситуация, когда производный класс должен скрывать...?

Возможно, глупый вопрос, но при условии, что base class A определяет a virtual method V, существует ли когда-либо ситуация, когда было бы целесообразно скрыть derived class C A.V, объявив новый virtual method C.V с той же сигнатурой как A.V:

class Program
{
    static void Main(string[] args)
    {
        A a = new C();
        a.Print(); // prints "this is in B class"

        C c = new C();
        c.Print();// prints "this is in C class"
    }
}

class A
{
    public virtual void Print()
    {
        Console.WriteLine("this is in A class");
    }
}

class B:A
{
    public override void Print()
    {
        Console.WriteLine("this is in B class");
    }
}

class C : B
{
    public virtual void Print()
    {
        Console.WriteLine("this is in C class");
    }
}

Спасибо

4b9b3361

Ответ 1

Скрытие унаследованных виртуальных машин не является чем-то, что должно быть сделано как часть продуманного дизайна. Языки поддерживают виртуальное скрытие, чтобы сделать рамки объектов более устойчивыми к будущим изменениям.

Пример: Release 1 объектной инфраструктуры X не предоставляет функцию Print(). Боб решает расширить несколько объектов инфраструктуры X, определив функции Print() в собственных классах потомков. Поскольку он планирует переопределить их в более конкретных классах, он также делает функцию Print() виртуальной.

Позже выйдет Release 2 объектной инфраструктуры X. Боб решает модернизировать свой текущий проект, чтобы использовать Release 2 вместо Release 1. Без ведома Боба, инфраструктура объекта X также решила, что Print() будет полезной функцией, и поэтому они добавили виртуальную Print() в один из базовые классы структуры.

С виртуальным укрытием классы потомков Боба, содержащие реализацию Bob Print(), должны компилироваться и выполняться нормально, даже если в базовых классах уже существует другой Print() - даже с другой сигнатурой метода. Код, который знает о базовом классе Print(), будет продолжать использовать его, и код, который знает о Bob Print(), будет продолжать использовать его. Никогда они не будут встречаться.

Без виртуального скрытия код Боба не будет компилироваться вообще, пока он не выполнит какую-то нетривиальную операцию по его исходному коду, чтобы устранить конфликт имен в Print(). Некоторые утверждают, что это "правильная" вещь (отказаться от компиляции), но реалистично любой rev базовой библиотеки, которая требует пересмотра существующего рабочего кода, не будет хорошо сочетаться с клиентами. Они будут обвинять рамки для того, чтобы сломать все и плохо говорить об этом.

Было бы разумно, чтобы Боб получил предупреждение компилятора о том, что базовая печать закрыта печатью Боба, но это не фатальная ошибка. Это то, что Боб должен, вероятно, очистить (переименовать или устранить его функцию Print()) как можно скорее, чтобы избежать путаницы человека.

Ответ 2

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

public interface IFoo { }

public class ConcreteFoo : IFoo { }

public abstract class Bar
{
    private IFoo m_Foo;

    public IFoo Foo
    {
        get { return m_Foo; }
    }

    protected void SetFoo(IFoo foo)
    {
        m_Foo = foo;
    }
}

public class ConcreteBar : Bar
{
    public ConcreteA(ConcreteFoo foo)
    {
        SetFoo(foo);
    }

    public new ConcreteFoo Foo
    {
        get { return (ConcreteFoo)base.Foo; }
    } 
}

В этом случае ссылка на ConcreteBar может извлечь ссылку на ConcreteFoo без явного приведения, в то время как ссылки Bar и IFoo не являются мудрее, поэтому вся их нормальная магия полиморфизма все еще применяется.

Ответ 3

Имеет ли смысл все зависит от класса. Если функциональность базового класса - это то, что вы не хотите использовать для пользователей вашего класса, тогда вы должны скрыть его, чтобы они не совершали с ним никаких ошибок. Хотя это обычно, когда вы потребляете API, с которым у вас нет слишком большого контроля. Если это то, что вы пишете, вероятно, лучше просто вернуться и изменить доступ к методам.

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

Ответ 4

Чтобы присоединиться к хору, я согласен с этим плохой идеей. Большинство примеров, которые я видел, это то, что разработчику хотелось бы, чтобы метод базового класса был виртуальным, но, к сожалению, он не был. Затем вы объявляете его новым и надеетесь, что никто не вызывает этот метод с помощью указателя базового класса на экземпляр объекта. Способность принять метод virtual и переопределить его как new virtual, что категория награды Дарвина.

Помимо разработчиков, new также путает инструменты обфускации. Поэтому будьте осторожны.

Тем не менее, я недавно нашел одно полезное приложение для члена new: я использовал его для переопределения общедоступного свойства типа readtly типа интерфейса ISomthing как свойства, которое вернуло фактический тип ConcreteSomething. Оба возвращали точно такую ​​же ссылку на объект, за исключением того, что в последнем случае объект возвращается как сам, а не указатель интерфейса. Он спас много многих понижений.

Ответ 5

Ну, во-первых, если вы действительно хотите спрятаться, вам нужно ключевое слово new. Вы не хотите просто добавить туда virtual.

class C : B
{
    public new void Print()
    {
        Console.WriteLine("this is in C class");
    }
}

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

И почему вы это сделаете: если бы вы написали класс A самостоятельно, я бы сказал, что у вас нет реальной причины. Но если вы этого не сделали, у вас может быть веская причина, по которой вы не хотите, чтобы этот метод вызывался. В одном проекте, над которым я работаю, у меня есть класс, полученный из List<>, но я не хочу, чтобы вызов Add вызывался, мне нужен специальный метод, чтобы я мог лучше контролировать элементы, которые добавляются. В этом случае я скрыл new и сделал его броском NotSupportedException, с сообщением, чтобы использовать другой метод.