Наследование и видимость членов - PullRequest
3 голосов
/ 13 января 2012

Предположим, что у нас есть 3 класса: Base, Root и Child.

public abstract class Base
{
    protected int _Value;

    public double DoSomeWork(int value)
    {
        _Value = value;
        double result = Calculate();

        return result;
    }

    public abstract double Calculate();

    public Child CreateChild(int length)
    {
        return new Child(this);
    }
}

public class Root : Base
{
    public override double Calculate()
    {
        return _Value;
    }
}

public class Child : Base
{
    readonly Base _Container;

    public Child(Base container)
    {
        _Container = container;
    }

    public override double Calculate()
    {
        double result = _Container.Calculate();
        // do some more calculation

        return result;
    }
}

Моя проблема здесь в том, что я хотел бы, чтобы только DoSomeWorkCreateChild) были общедоступными, но в моей "архитектуре" я вынужден также сделать Calculate публичной Или я?

Любой вклад будет очень признателен.

Edit:

Calculate невозможно защитить из-за этой строки в Child

double result = _Container.Calculate();

, что может привести к ошибке сборки.

Ответы [ 8 ]

8 голосов
/ 13 января 2012

Давайте уберем ненужные слова из вашего примера:

public abstract class Base
{
    public abstract double Calculate();
}
public class Derived : Base
{
    private Base b;
    public override double Calculate()
    {
        double r = b.Calculate();
        // Perform additional calculations on r
        return r;
    }
}

Вопрос в том, должен ли Calculate быть публичным?

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

Вычислить нельзя защитить, потому что вызов b.Calculate() в Derived является недопустимым, если во время компиляции не известно, что он является экземпляром Derived или лучше. (Причина в том, что защищенные члены доступны для подклассов; объект, на который ссылается b, может иметь совершенно другой подкласс Base, и Derived не разрешен доступ к защищенному методу этого класса, поскольку Derived не является его производным.)

Расчет может быть внутренним, внутренним защищенным или общедоступным.

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

public abstract class Base
{
    protected static double Calculate(Base b) 
    { 
        // perfectly legal inside Base:
        return b.Calculate(); 
    }        
    protected abstract double Calculate();
}
public class Derived : Base
{
    private Base b;
    protected override double Calculate()
    {
        double r = Base.Calculate(b);
        // Perform additional calculations on r
        return r;
    }
}

Имеет смысл?

6 голосов
/ 13 января 2012

Нет, вы можете сделать это protected. Таким образом, ваш производный класс по-прежнему будет иметь к нему доступ и сможет его переопределить, но это не будет разрешено публично.

Хорошее мышление, это именно то, что нужно сделать: переопределить как можно меньше и спрятать как можно больше.

- Правка -

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

У меня такое ощущение, что метод Calculate может не принадлежать вашему классу Base. Похоже, что он обеспечивает вспомогательный результат вычисления, используемый DoSomeWork. Наследование обычно используется, когда ваш базовый класс имеет некоторые общие вычисления, чтобы «предложить» производным классам, чтобы вам не пришлось повторяться.

Например, предположим, что ваш метод DoSomeWork имеет некоторые определенные функциональные возможности, которые не меняются, но требуют, чтобы сначала выполнялись "внешние" вычисления. Если вы начали с создания простого отдельного интерфейса для внешнего расчета:

interface ICalculator
{
    double Calculate();
}

Вы можете определить различные реализации этого интерфейса. Вы можете начать с создания простой реализации (аналогично вашей Root функциональности):

class SimpleCalculator : ICalculator
{
    readonly double _value;
    public SimpleCalculator(double value)
    {
       _value = value;
    }

    public double Calculate()
    {
       return _value;
    }
}

И вы также можете легко обернуть существующие реализации в более сложные классы (аналогично тому, что CreateChild намеревается сделать):

// for the rest of the world, this is an ICalculator like any other
class CalculatorWrapper : ICalculator
{ 
    readonly ICalculator _base;
    public CalculatorWrapper(ICalculator baseCalc)
    {
       _base = baseCalc;
    }

    public double Calculate()
    {
       double value = _base.Value; 
       return 2 * value;
    }
}

А затем, ваш фактический класс должен использовать эту функциональность для некоторой предполагаемой «дополнительной работы»:

interface MyWorker
{
    double DoSomeWork(int value);
}

class YourClass
{
    readonly ICalculator _calc; 
    readonly double _someOtherValue;

    public YourClass(ICalculator calc, double someOtherValue)
    {
       _calc = calc;
       _someOtherValue = someOtherValue;
    }

    public double DoSomeWork(int value)
    {
       // use whatever you get from your calc
       double externalValue = _calc.Calculate();

       // and do some "actual work"
       return _someOtherValue + value + externalValue;
    }
}

или , вы можете передать «калькулятор» в DoSomeWork при каждом вызове в качестве параметра и изменить комплексный класс на что-то вроде:

interface MyWorker
{
    double DoSomeWork(ICalculator calc, int value);
}

class YourClass
{
    public double DoSomeWork(ICalculator calc, int value)
    {
       // use whatever you get from your calc
       double externalValue = calc.Calculate();

       // and do some "actual work"
       return _someOtherValue + value;
    }
}
1 голос
/ 13 января 2012

Не должно быть public.Это можно сделать protected.

1 голос
/ 13 января 2012

Вы можете объявить Calculate как protected, и он будет доступен только для расширяющихся классов.

Это та же концепция, что и protected int _Value; - у вас есть доступ к нему в текущем классе и всеего дети, но не снаружи.

0 голосов
/ 13 января 2012

Вы можете сделать это:

  1. protected - Доступно только для производных от него типов.
  2. internal - Доступно только для типов в одной сборке (или сборке со строгим именем, объявленной как разрешенная для просмотра внутренних типов этой сборки).
  3. protected internal - Позволяет оба вышеперечисленных; Производные типы и другие типы в одной сборке могут получить к ней доступ.

.NET также имеет тип доступа, который ограничивает то, что он должен быть одновременно производным типом и в той же сборке, но C # не поддерживает его (возможно, потому что в этом случае вы можете обойтись с internal было бы сложно придумать имя, которое нелегко спутать с protected internal).

В этом случае это почти наверняка должно быть protected.

0 голосов
/ 13 января 2012

Вы можете использовать protected (= только внутри класса и в его доступных унаследованных классах) или internal (= только внутри той же самой сборки, доступной). Смотри ЗДЕСЬ .

0 голосов
/ 13 января 2012

Нет, ты мог бы сделать это protected

0 голосов
/ 13 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...