Какие модификаторы доступа ты знаешь в C#?

Ответ

В C# существует шесть модификаторов доступа, которые определяют область видимости и доступность типов (классов, структур, интерфейсов) и их членов (полей, свойств, методов).

Модификатор Доступность Область применения по умолчанию
public Не ограничена. Доступен из любого кода в той же сборке (assembly) или в других сборках, которые на нее ссылаются. Члены интерфейса, перечисления.
private Доступен только внутри объявляющего типа (класса или структуры). Члены класса и структуры.
protected Доступен внутри объявляющего типа и во всех производных (наследующих) классах. Производный класс может находиться в другой сборке. -
internal Доступен из любого кода в той же сборке, но не из других сборок. Типы (классы, структуры) без явного модификатора.
protected internal Объединение protected ИЛИ internal. Доступен: 1) из кода в той же сборке, 2) из производных классов (даже в других сборках). -
private protected (C# 7.2+) Пересечение protected И internal. Доступен только из производных классов, которые находятся в той же сборке. -

Практические примеры и сценарии использования:

// Файл: AssemblyA.dll
public class BaseClass
{
    public int PublicField;           // Виден всем.
    private int _privateField;        // Виден только внутри BaseClass.
    protected int ProtectedField;     // Виден в BaseClass и DerivedClass (даже в другом DLL).
    internal int InternalField;       // Виден во всей AssemblyA.dll.
    protected internal int ProtIntField; // Виден в AssemblyA.dll И в DerivedClass из любого DLL.
    private protected int PrivProtField; // Виден ТОЛЬКО в BaseClass и DerivedClass внутри AssemblyA.dll.
}

internal class InternalClass { } // Класс виден только внутри AssemblyA.dll.

// Файл: AssemblyB.dll (ссылается на AssemblyA.dll)
public class DerivedClass : BaseClass
{
    public void Demo()
    {
        PublicField = 1;      // OK
        // _privateField = 2; // ОШИБКА: недоступно
        ProtectedField = 3;   // OK (наследование)
        // InternalField = 4; // ОШИБКА: другая сборка
        ProtIntField = 5;     // OK (наследование работает)
        // PrivProtField = 6; // ОШИБКА: другая сборка (нарушено условие "private protected")
    }
}

// В другом классе AssemblyB.dll
class AnotherClass
{
    void Demo()
    {
        var obj = new BaseClass();
        obj.PublicField = 1;  // OK
        // obj.ProtectedField = 2; // ОШИБКА: нет наследования
        // obj.InternalField = 3;  // ОШИБКА: другая сборка
        // obj.ProtIntField = 4;   // ОШИБКА: другая сборка И нет наследования
    }
}

Рекомендации:

  • Соблюдайте принцип минимального доступа: начинайте с private и расширяйте видимость только при необходимости.
  • public интерфейс класса должен быть минимальным и стабильным.
  • Используйте internal для скрытия служебных типов и методов, которые не должны быть частью публичного API вашей библиотеки.
  • private protected полезен в больших проектах для создания "семейного" доступа внутри одной сборки.

Ответ 18+ 🔞

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

Вот они, эти модификаторы, смотри, разложу по полочкам:

Модификатор Кто может лезть? Где применяется, если не указать?
public Абсолютно все, кому не лень. Из своей сборки, из чужой — всем похую, бери и пользуйся. Члены интерфейсов и перечислений.
private Только внутри того класса или структуры, где объявил. Всё, точка. Как будто на двери замок повесил. Члены классов и структур.
protected Сам класс плюс все его дети-наследники, даже если они в другой сборке живут. Семейное дело, понимаешь? -
internal Любой код внутри этой же сборки. Для внешнего мира — как будто этого кода не существует. Типы (классы, структуры), если явно не указать.
protected internal Это как ИЛИ. Доступно либо из той же сборки, либо наследникам откуда угодно. Широкая душа, блядь. -
private protected (C# 7.2+) А это уже И. Доступно только наследникам, и только если они в той же сборке. Узкий семейный круг, епта. -

А теперь, сука, пример, чтобы вообще всё встало на свои места:

// Файл: AssemblyA.dll
public class BaseClass
{
    public int PublicField;           // Виден всем, как хуй с горы.
    private int _privateField;        // Виден только тут. Свой секрет.
    protected int ProtectedField;     // Мне и моим детям.
    internal int InternalField;       // Всем своим в этой сборке.
    protected internal int ProtIntField; // Или своим в сборке, или детям откуда угодно.
    private protected int PrivProtField; // Только детям, которые тут же, в этой сборке. Больше никому.
}

internal class InternalClass { } // Этот класс снаружи сборки — просто призрак, его нет.

// Файл: AssemblyB.dll (тыкнули ссылку на AssemblyA.dll)
public class DerivedClass : BaseClass
{
    public void Demo()
    {
        PublicField = 1;      // Окей, это публичное.
        // _privateField = 2; // Нихуя! Это папино личное, тебе не светит.
        ProtectedField = 3;   // Окей, ты же наследник, семья.
        // InternalField = 4; // Бля, нет! Ты уже в другой сборке, это internal.
        ProtIntField = 5;     // Окей! Ты наследник (protected часть сработала).
        // PrivProtField = 6; // А вот тут облом. Ты наследник, но из другой сборки. private protected говорит "нет".
    }
}

// Просто какой-то левый класс в AssemblyB.dll
class AnotherClass
{
    void Demo()
    {
        var obj = new BaseClass();
        obj.PublicField = 1;  // Окей, публичное.
        // obj.ProtectedField = 2; // Не, дружок, ты не родственник.
        // obj.InternalField = 3;  // И не земляк по сборке.
        // obj.ProtIntField = 4;   // И не то, и не другое. Иди нахуй.
    }
}

Итог, блядь, и главные правила:

  • Принцип минимального доступа — твой друг. Начинай всегда с private. Зачем светить на весь мир то, что нужно только твоему классу? Расширяешь доступ только когда реально приперло.
  • public интерфейс делай маленьким и надёжным. Это лицо твоего класса, его не каждый день меняют.
  • internal — твоя палочка-выручалочка, чтобы спрятать служебную хуйню от пользователей твоей библиотеки. Пусть думают, что там магия.
  • private protected — штука специфичная. Полезно в больших проектах внутри одной сборки, когда хочешь дать доступ только по семейной линии и никому больше.