不只是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# 技术干货!如果觉得有用,记得收藏本文

相关推荐
涔溪1 小时前
Vue3常用的组合式API 超详细讲解
前端·javascript·vue.js
秋邱1 小时前
AR + 离线 AI 实战:YOLOv9+TensorFlow Lite 实现移动端垃圾分类识别
开发语言·前端·数据库·人工智能·python·html
蜡笔小嘟1 小时前
使用gemini 3 pro实现可视化大屏
前端·ai·gemini·gemini3peo
马玉霞1 小时前
vue3很丝滑的table表格向上滚动效果,多用于统计页面
前端·vue.js
用户952081611791 小时前
百度地图JSAPI THREE Label 组件使用指南,轻松实现地图标签渲染
前端
SVIP111591 小时前
webpack入门 精细版
前端·webpack·node.js
秋雨雁南飞1 小时前
c# 多项目编译发布
c#
畅畅畅哥哥1 小时前
Next.js App Router 实战避坑:状态、缓存与测试
前端·前端框架
一水鉴天1 小时前
整体设计 定稿 之20 拼语言表述体系之3 dashboard.html完整代码
java·前端·javascript