Почему внутренние поля предпочтительнее защищенных полей в абстрактном классе? - PullRequest
0 голосов
/ 02 мая 2020

У меня есть абстрактный класс для устройства с последовательным портом:

public abstract class SerialDevice
{
    // serial port (should this be protected, internal, or protected internal?)
    protected SerialPort _serialPort;

    // The serial port has some shared methods.
    public void Open()
    {
        _serialPort.Open();
    }
}

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

public class Widget : SerialDevice
{
    // The serial port has some widget-specific functionality.
    public void UniqueCommand()
    {
        _serialPort.WriteLine("Hello world.");
    }
}

Когда я компилирую, я получаю это предупреждение: CA1051: Не объявляйте видимые поля экземпляра , который говорит, что поля должны быть скрытой деталью реализации. Хорошо, я могу вместо этого использовать защищенное свойство и защитить свои классы от изменений, которые нарушают работу.

Но рекомендуется продолжать использовать поля типа private или внутренние . На странице C# в Руководстве по программированию по модификаторам доступа говорится, что если сделать последовательный порт «защищенным», он будет доступен только внутри производных классов, а если он будет «внутренним», то он будет доступен в целом. сборка. Так почему «внутренний» приемлем, а «защищенный» - нет? Разве защищенный доступ не менее видим, чем внутренний доступ?

Смежный вопрос: Какой должна быть доступность полей в абстрактном классе?

Ответы [ 3 ]

1 голос
/ 02 мая 2020

Чтобы добиться инкапсуляции, мы не должны открывать наши поля непосредственно снаружи, даже классу в нашей собственной сборке.

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

Если мы go сделаем еще один шаг и хотим, чтобы абстрактный / базовый класс расширялся только классами в нашей собственной сборке:

Имея базовый класс publi c и указав все конструкторы как внутренние, мы можем быть уверены, что только классы в нашей собственной сборке смогут его расширять.

public abstract class SerialDevice
{
    private SerialPort _serialPort;

    internal SerialPort GetSerialPort()
    {
        return _serialPort;
    }

    // We define our base class public,
    // but also define all the constructors internal
    // Therefore, no class outside our assembly extend it
    // but we still can expose sub classes (like Widget) to the outside
    internal SerialDevice()
    {

    }

    // The serial port has some shared methods.
    // These methods are still visible from the outside
    public void Open()
    {
        _serialPort.Open();
    }
}

И,

public class Widget : SerialDevice
{
    // The serial port has some widget-specific functionality.
    public void UniqueCommand()
    {
        GetSerialPort().WriteLine("Hello world.");
    }
}
1 голос
/ 02 мая 2020

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

0 голосов
/ 05 мая 2020

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

  • Абстрактный класс - publi c, поэтому остальная часть сборки может его увидеть.
  • Абстрактный Конструктор класса является внутренним, поэтому он не может быть унаследован вне сборки.
  • Последовательный порт «Порт» является свойством, а не полем, поэтому его можно изменить позже в абстрактном классе. не нарушая ни один из производных классов.
  • Свойство «Порт» защищено, поэтому его нельзя увидеть, кроме как внутри классов, производных от базового класса.
public abstract class SerialDevice
{
    // Constructor
    internal SerialDevice() {}

    // serial port
    protected SerialPort Port { get; } = new SerialPort();

    // The serial port has some shared methods.
    public void Open()
    {
        Port.Open();
    }
}
public class Widget : SerialDevice
{
    // The serial port has some widget-specific functionality.
    public void UniqueCommand()
    {
        Port.WriteLine("Hello world.");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...