Вопрос по c# – C # - Могут ли публично наследуемые методы быть скрытыми (например, сделанными закрытыми для производного класса)

54

Предположим, у меня есть BaseClass с открытыми методами A и B, и я создаю DerivedClass с помощью наследования.

например

public DerivedClass : BaseClass {}

Теперь я хочу разработать метод C в DerivedClass, который использует A и B. Есть ли способ, которым я могу переопределить методы A и B, чтобы они были частными в DerivedClass, чтобы только метод C был доступен для того, кто хочет использовать мой DerivedClass?

Ваш Ответ

10   ответов
22

List<object>и вы хотите скрыть прямыеAdd(object _ob) член:

// the only way to hide
[Obsolete("This is not supported in this class.", true)]
public new void Add(object _ob)
{
    throw NotImplementedException("Don't use!!");
}

Это на самом деле не самое предпочтительное решение, но оно выполняет свою работу. Intellisense по-прежнему принимает, но во время компиляции вы получаете ошибку:

error CS0619: 'TestConsole.TestClass.Add(TestConsole.TestObject)' is obsolete: 'This is not supported in this class.'

Error: User Rate Limit Exceeded
3

Has-A и реализовывать только те функции, которые вы хотите предоставить.

0

вы не можете переопределить их как частные в вашем производном классе. Тем не менее, вы можете сделать так, чтобы открытый метод вызывал исключение и реализовывал вашу собственную частную функцию.

Редактировать: Хорхе Феррейра правильно.

6

Лисков не будет впечатлен.

Если вы не хотите, чтобы потребители DerivedClass могли иметь доступ к методам DeriveClass.A () и DerivedClass.B (), я хотел бы предложить, чтобы DerivedClass реализовал некоторый открытый интерфейс IWhwhatMethodCIsAbout, а потребители DerivedClass должны фактически общаться с IWh независимоMethodCIsAbout и ничего о реализации BaseClass или DerivedClass вообще.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Lyndon
3

@ Brian R.new ключевое слово.

http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx

В качестве обходного пути я бы предложил:

class BaseClass
{
    public void A()
    {
        Console.WriteLine("BaseClass.A");
    }

    public void B()
    {
        Console.WriteLine("BaseClass.B");
    }
}

class DerivedClass : BaseClass
{
    new public void A()
    {
        throw new NotSupportedException();
    }

    new public void B()
    {
        throw new NotSupportedException();
    }

    public void C()
    {
        base.A();
        base.B();
    }
}

Этот код, как этот, будетNotSupportedException:

    DerivedClass d = new DerivedClass();
    d.A();
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Lyndon
Error: User Rate Limit Exceeded
5

What you need is composition not inheritance.

Теперь, если вам нужен специальный вид Plane, такой как, который имеет PairOfWings = 2, но в остальном делает все, что может плоскость. Вы наследуете плоскость. Этим вы заявляете, что ваше деривация соответствует контракту базового класса и может быть заменена без мерцания везде, где ожидается базовый класс. например LogFlight (Plane) будет продолжать работать с экземпляром BiPlane.

Однако, если вам просто нужно поведение Fly для новой Птицы, которую вы хотите создать и не желаете поддерживать полный контракт базового класса, вы пишете вместо этого. В этом случае рефакторинг поведения методов для повторного использования в новом типе Flight. Теперь создайте и удерживайте ссылки на этот класс в Plane и Bird. Вы не наследуете, потому что Bird не поддерживает полный контракт базового класса ... (например, он не может предоставить GetPilot ()).

По той же причине,you cannot reduce the visibility of base class methods when you override.. вы можете переопределить и сделать базовый приватный метод общедоступным в деривации, но не наоборот. например В этом примере, если я получу тип плоскости «BadPlane» а затем переопределите и & quot; Скрыть & quot; GetPilot () - сделать его приватным; клиентский метод LogFlight (Plane p) будет работать для большинства плоскостей, но будет взорван для «BadPlane». если для реализации LogFlight потребуется / вызовите GetPilot ().Since all derivations of a base class are expected to be 'substitutable' wherever a base class param is expected, this has to be disallowed.

Error: User Rate Limit Exceeded
0

есть один совет, на который я хотел бы обратить внимание для других, прибывающих сюда (учитывая, что OP как бы намекает на доступ к сборке третьими лицами). Когда другие ссылаются на сборку, Visual Studio должен учитывать следующий атрибут, чтобы он не отображался в intellisense (скрытый, но можно вызывать STILL, поэтому будьте осторожны):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

Если у вас не было другого выбора, вы должны иметь возможность использоватьnew на метод, который скрывает метод базового типа, вернуть=> throw new NotSupportedException();и объедините его с атрибутом выше.

Другой трюк зависит от НЕ наследования от базового класса, если это возможно, где база имеет соответствующий интерфейс (такой какIList<T> заList<T>). Реализация интерфейсов "явно" также будет скрывать эти методы от intellisense для типа класса. Например:

public class GoodForNothing: IDisposable
{
    void IDisposable.Dispose() { ... }
}

В случаеvar obj = new GoodForNothing(),Dispose() метод не будет доступен наobj, Тем не менее, он будет доступен для всех, кто явно набирает типobj вIDisposable.

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

public class MyList<T> : IList<T>
{
    List<T> _Items = new List<T>();
    public T this[int index] => _Items[index];
    public int Count => _Items.Count;
    public void Add(T item) => _Items.Add(item);
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden)
    /*...etc...*/
}
68

It's not possible, why?

В C # вы обязаны, если вы наследуете публичные методы, вы должны сделать их публичными. В противном случае они ожидают, что вы не будете выходить из класса в первую очередь.

Вместо использования отношения is-a вы должны использовать отношение has-a.

Разработчики языка не допускают этого специально, чтобы вы более правильно использовали наследование.

Например, можно случайно спутать класс Car с производным от класса Engine, чтобы получить его функциональность. Но двигатель - это функциональность, которую использует автомобиль. Таким образом, вы хотели бы использовать отношения has-a. Пользователь Автомобиля не хочет иметь доступ к интерфейсу Двигателя. И сам автомобиль не должен смешивать методы двигателя с его собственными. Будущие дериваты ни Car.

Таким образом, они не позволяют защитить вас от неправильной иерархии наследования.

What should you do instead?

Вместо этого вы должны реализовать интерфейсы. Это освобождает вас от функциональности, используя отношения has-a.

Other languages:

В C ++ вы просто указываете модификатор перед базовым классом private, public или protected. Это делает всех членов базы общедоступными для указанного уровня доступа. Мне кажется глупым, что вы не можете делать то же самое в C #.

The restructured code:

interface I
{
    void C();
}

class BaseClass
{
    public void A() { MessageBox.Show("A"); }
    public void B() { MessageBox.Show("B"); }
}

class Derived : I
{
    public void C()
    {
        b.A();
        b.B();
    }

    private BaseClass b;
}

Я понимаю, что названия вышеперечисленных классов немного спорный :)

Other suggestions:

Другие предложили сделать A () и B () общедоступными и выбросить исключения. Но это не делает дружественный класс для использования людьми, и это не имеет смысла.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
1

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

Предстоящая парадигма кодирования называется «Композиция по наследованию». Это напрямую противоречит принципам объектно-ориентированной разработки (особенно принципу единой ответственности и принципу открытой / закрытой).

К сожалению, так как многие из нас, разработчиков, учили объектной ориентации, у нас сформировалась привычка немедленно думать о наследовании вместо композиции. У нас, как правило, большие классы, на которых лежит много разных обязанностей, просто потому, что они могут содержаться в одном и том же «реальном мире». объект. Это может привести к иерархии классов более 5 уровней.

К сожалению, побочный эффект, о котором обычно не думают разработчики при работе с наследованием, заключается в том, что наследование является одной из самых сильных форм зависимостей, которые вы когда-либо можете ввести в свой код. Ваш производный класс теперь сильно зависит от класса, от которого он был унаследован. Это может сделать ваш код более хрупким в долгосрочной перспективе и привести к сложным проблемам, когда изменение определенного поведения в базовом классе нарушает производные классы непонятными способами.

Один из способов разбить ваш код - использовать интерфейсы, подобные упомянутому в другом ответе. В любом случае это разумно, так как вы хотите, чтобы внешние зависимости класса связывались с абстракциями, а не с конкретными / производными типами. Это позволяет вам изменять реализацию без изменения интерфейса, и все это без влияния на строку кода в вашем зависимом классе.

Я бы предпочел бы не поддерживать систему с сотнями / тысячами и даже большим количеством классов, которые все малы и слабо связаны, чем иметь дело с системой, которая интенсивно использует полиморфизм / наследование и имеет меньше классов, которые более тесно связаны.

Возможно,best ресурсом по объектно-ориентированной разработке является книга Роберта Мартина,Гибкая разработка программного обеспечения, принципы, шаблоны и практики.

1

IMO, являются:

It's dependent upon the design-time declaration type of the instance, meaning if you do something like BaseClass obj = new SubClass(), then call obj.A(), hiding is defeated. BaseClass.A() will be executed.

Hiding can very easily obscure behavior (or behavior changes) in the base type. This is obviously less of a concern when you own both sides of the equation, or if calling 'base.xxx' is part of your sub-member.

If you actually do own both sides of the base/sub-class equation, then you should be able to devise a more manageable solution than institutionalized hiding/shadowing.

Похожие вопросы