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

С# - Можно ли скрывать скрытые методы (например, сделанные частными для производного класса)

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

например.

public DerivedClass : BaseClass {}

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

4b9b3361

Ответ 1

Это невозможно, почему?

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

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

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

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

Поэтому они не позволяют защитить вас от неправильных иерархий наследования.

Что вы должны делать вместо этого?

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

Другие языки:

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

Измененный код:

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;
}

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

Другие предложения:

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

Ответ 2

Если вы, например, попытаетесь наследовать от 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 по-прежнему принимает, но во время компиляции вы получаете сообщение об ошибке:

ошибка CS0619: "TestConsole.TestClass.Add(TestConsole.TestObject)" устарела: "Это не поддерживается в этом классе."

Ответ 3

Это звучит неплохо. Liskov не будет впечатлен.

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

Ответ 4

Вам нужна композиция, а не наследование.

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

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

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

По той же причине вы не можете уменьшить видимость методов базового класса при переопределении. вы можете переопределить и сделать базовый частный метод общедоступным в деривации, но не наоборот. например В этом примере, если я получаю тип Plane "BadPlane", а затем переопределяет и "скрывает" GetPilot() - делает его приватным; клиентский метод LogFlight (Plane p) будет работать для большинства Planes, но взорвется для "BadPlane", если для выполнения LogFlight потребуется/вызвать GetPilot(). Так как ожидается, что все производные от базового класса будут "заменяемыми" везде, где ожидается базовый класс param, это должно быть запрещено.

Ответ 5

Единственный способ сделать это, о котором я знаю, - использовать отношения Has-A и реализовывать только те функции, которые вы хотите открыть.

Ответ 6

@Brian R. Bondy указал мне на интересную статью о скрытии через наследование и ключевое слово 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();

Ответ 7

Скрытие - довольно скользкий склон. Основные проблемы, IMO:

  • Это зависит от времени разработки тип объявления экземпляра, это означает, что если вы делаете что-то вроде BaseClass obj = new SubClass(), затем вызов obj.A(), сокрытие побеждено. Запускается BaseClass.A().

  • Скрытие может очень легко скрываться поведение (или изменения поведения) в базовый тип. Это очевидно меньше заботы, когда вы владеете обоими стороны уравнения, или если вызов base.xxx является частью вашего подчастица.

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

Ответ 8

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

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

К сожалению, так, как многие из нас разработчики обучались объектной ориентации, у нас сложилась привычка сразу думать о наследовании, а не о композиции. Мы склонны иметь более крупные классы, которые имеют много разных обязанностей, просто потому, что они могут содержаться с одним и тем же объектом "Реальный мир". Это может привести к иерархиям классов, которые имеют глубину более 5+.

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

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

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

Возможно, ресурс лучший для объектно-ориентированного развития - это книга Роберта К. Мартина, Agile Software Development, Principles, Шаблоны и практики.

Ответ 9

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

Изменить: Хорхе Феррейра прав.