不只是Public与Private:C#访问修饰符全方位解读

一、访问修饰符的控制范围

1.public:完全公开

  • 访问范围 :无限制。任何代码,无论是在同一个程序集(Assembly)还是在其他程序集中,都可以访问被public修饰的成员。
  • 适用对象:类、结构、接口、枚举、委托、以及类的成员(方法、属性、字段等)。
  • 用途:用于定义一个类的"公共API"。这些是你希望提供给外部世界使用的功能。
csharp 复制代码
// 在 AssemblyA.dll 中
public class Calculator
{
    public int Add(int a, int b) // 任何代码都可以调用这个方法
    {
        return a + b;
    }
}
csharp 复制代码
// 在另一个项目 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

class Program
{
    static void Main(string[] args)
    {
        Calculator calc = new Calculator();
        int result = calc.Add(2, 3); // 可以访问
        Console.WriteLine(result);
    }
}

2.private:绝对私有

  • 访问范围 :仅限在声明它的类或结构内部。
  • 适用对象 :类的成员(嵌套类、方法、属性、字段等)。注意:顶层类型(非嵌套类)不能是private的。
  • 用途 :封装的基石。用于隐藏类的内部状态和实现细节。一个类中绝大部分的字段都应该是private的。
csharp 复制代码
public class BankAccount
{
    private decimal _balance; // 只能在BankAccount类内部访问

    public void Deposit(decimal amount)
    {
        if (amount > 0)
        {
            _balance += amount; // 内部可以访问
        }
    }
}

class Test
{
    void DoSomething()
    {
        BankAccount account = new BankAccount();
        // account._balance = 10000; // 编译错误!无法从外部访问_balance
    }
}

3.protected:家族内部的秘密

  • 访问范围 :在声明它的内部,以及**该类的任何子类(派生类)**中。
  • 适用对象:类的成员。
  • 用途:为了让子类能够访问和扩展父类的功能,同时又不想让这些功能对外界完全公开。它在继承体系中扮演着关键角色。
csharp 复制代码
// 在 AssemblyA.dll 中
public class Animal
{
    protected string Name { get; set; } // 子类可以访问Name

    protected void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

public class Dog : Animal
{
    public Dog(string name)
    {
        this.Name = name; // 正确:子类可以访问受保护的成员
    }

    public void Bark()
    {
        Console.WriteLine("Woof!");
        this.Eat(); // 正确:子类可以调用受保护的方法
    }
}
csharp 复制代码
// 在 AssemblyA.dll 或其他程序集中
class Test
{
    void DoSomething()
    {
        Animal animal = new Animal();
        // animal.Name = "Tom"; // 编译错误!外部无法访问protected成员
        // animal.Eat();      // 编译错误!

        Dog dog = new Dog("Buddy");
        // dog.Name = "Lucy"; // 编译错误!即使通过子类实例,外部也无法访问
    }
}

关键点protected的访问权限是基于继承关系 的,而不是实例。在Test类中,即使有一个Dog的实例,也不能访问其protected成员,因为Test类不是Animal的子类。

4. nternal:同一个"项目"里的朋友

  • 访问范围:仅限在同一个**程序集(Assembly)**内部。一个程序集通常对应一个项目(.csproj),编译后生成一个DLL或EXE文件。
  • 适用对象:顶层类型和类的成员。
  • 用途 :当你想在同一个项目或组件内部共享一些类或工具方法,但又不希望将它们暴露给引用这个组件的外部项目时,internal是最佳选择。这是创建框架或库时非常有用的一个修饰符。
csharp 复制代码
// 在 AssemblyA.dll 中
internal class InternalHelper
{
    public static void DoWork()
    {
        Console.WriteLine("Internal work done.");
    }
}

public class PublicFacade
{
    public void PerformAction()
    {
        InternalHelper.DoWork(); // 同一个程序集内,可以访问internal类
    }
}
csharp 复制代码
// 在另一个项目 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

class Program
{
    static void Main(string[] args)
    {
        PublicFacade facade = new PublicFacade();
        facade.PerformAction(); // 可以调用

        // InternalHelper helper = new InternalHelper(); // 编译错误!InternalHelper在AssemblyB中不可见
    }
}

默认访问级别 :如果你在声明一个顶层类(非嵌套)时不写任何访问修饰符 ,它的默认访问级别就是internal

csharp 复制代码
// 等同于 internal class MyClass { }
class MyClass { } 

5. 两种组合修饰符:更精细的控制

C#还提供了两种由protectedinternal组合而成的修饰符,用于实现更复杂的访问控制场景。

5.1 protected internal:家族朋友

  • 访问范围 :满足以下任一条件 即可访问:
    1. 在同一个程序集内。
    2. 在不同的程序集中,但必须是该类的子类。
  • 逻辑关系protected OR internal。这是最宽松的访问级别之一(仅次于public)。
  • 用途:当你希望一个成员主要在程序集内部使用,但同时也允许其他程序集中的子类对其进行扩展时。
csharp 复制代码
// 在 AssemblyA.dll 中
public class BaseClass
{
    protected internal int Value { get; set; }
}

public class DerivedInSameAssembly : BaseClass
{
    void Test()
    {
        this.Value = 10; // 可以访问,因为在同一个程序集
    }
}

internal class OtherClassInSameAssembly
{
    void Test()
    {
        BaseClass b = new BaseClass();
        b.Value = 20; // 可以访问,因为在同一个程序集
    }
}
csharp 复制代码
// 在 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

public class DerivedInOtherAssembly : BaseClass
{
    void Test()
    {
        this.Value = 30; // 可以访问,因为是子类
    }
}

class OtherClassInOtherAssembly
{
    void Test()
    {
        BaseClass b = new BaseClass();
        // b.Value = 40; // 编译错误!不在同一程序集,也不是子类
    }
}

5.2 private protected:本家的私密

  • 访问范围 :必须同时满足 以下两个条件才能访问:
    1. 在同一个程序集内。
    2. 必须是该类的子类。
  • 逻辑关系private AND protected(概念上),或者更准确地说是protected AND internal
  • 用途 :这是最严格的访问级别之一。当你希望某个成员只能被同一个项目中的子类访问时使用。这可以防止其他不相关的项目继承你的类并滥用这些受保护的成员。
csharp 复制代码
// 在 AssemblyA.dll 中
public class BaseClass
{
    private protected string SecretCode { get; set; }
}

public class DerivedInSameAssembly : BaseClass
{
    void Test()
    {
        this.SecretCode = "Alpha"; // 可以访问,同一程序集 + 子类
    }
}

internal class OtherClassInSameAssembly
{
    void Test()
    {
        BaseClass b = new BaseClass();
        // b.SecretCode = "Beta"; // 编译错误!虽然在同一程序集,但不是子类
    }
}
csharp 复制代码
// 在 AssemblyB.exe 中,引用了 AssemblyA.dll
using AssemblyA;

public class DerivedInOtherAssembly : BaseClass
{
    void Test()
    {
        // this.SecretCode = "Gamma"; // 编译错误!虽然是子类,但不在同一个程序集
    }
}

三、访问修饰符总结与对比

修饰符 当前类内部 同程序集内的子类 同程序集内的非子类 不同程序集的子类 不同程序集的非子类
public
protected internal
internal
protected
private protected
private

结语

点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文

相关推荐
xb113214 小时前
C# 定时器和后台任务
开发语言·c#
疯子****15 小时前
【无标题】
前端·clawdbot
RichardLau_Cx16 小时前
【保姆级实操】MediaPipe SDK/API 前端项目接入指南(Web版,可直接复制代码)
前端·vue·react·webassembly·mediapipe·手部追踪·前端计算机视觉
不爱写程序的东方不败16 小时前
APP接口测试流程实战Posman+Fiddler
前端·测试工具·fiddler
晚霞的不甘17 小时前
Flutter for OpenHarmony构建全功能视差侧滑菜单系统:从动效设计到多页面导航的完整实践
前端·学习·flutter·microsoft·前端框架·交互
黎子越17 小时前
python相关练习
java·前端·python
A_nanda17 小时前
c# 用VUE+elmentPlus生成简单管理系统
javascript·vue.js·c#
北极糊的狐17 小时前
若依项目vue前端启动键入npm run dev 报错:不是内部或外部命令,也不是可运行的程序或批处理文件。
前端·javascript·vue.js
XRJ040618xrj17 小时前
Nginx下构建PC站点
服务器·前端·nginx
We་ct17 小时前
LeetCode 289. 生命游戏:题解+优化,从基础到原地最优
前端·算法·leetcode·矩阵·typescript