文章目录
-
- 一、访问修饰符详解
-
- [1.1 访问修饰符对比表](#1.1 访问修饰符对比表)
- [1.2 访问修饰符详细说明](#1.2 访问修饰符详细说明)
-
- [**public - 公共访问**](#public - 公共访问)
- [**private - 私有访问**](#private - 私有访问)
- [**protected - 受保护访问**](#protected - 受保护访问)
- [**internal - 程序集内部访问**](#internal - 程序集内部访问)
- [**protected internal - 受保护的内部访问**](#protected internal - 受保护的内部访问)
- [**private protected - 私有受保护访问 (C# 7.2+)**](# 7.2+)**)
- [1.3 访问修饰符可视化对比](#1.3 访问修饰符可视化对比)
- 二、类修饰符详解
-
- [2.1 类修饰符对比表](#2.1 类修饰符对比表)
- [2.2 类修饰符详细说明](#2.2 类修饰符详细说明)
-
- [**abstract - 抽象类**](#abstract - 抽象类)
- [**sealed - 密封类**](#sealed - 密封类)
- [**static - 静态类**](#static - 静态类)
- [**partial - 部分类**](#partial - 部分类)
- [**new - 隐藏成员**](#new - 隐藏成员)
- [2.3 修饰符组合规则](#2.3 修饰符组合规则)
- 三、综合对比分析
-
- [3.1 访问修饰符选择指南](#3.1 访问修饰符选择指南)
- [3.2 类修饰符选择指南](#3.2 类修饰符选择指南)
- [3.3 完整示例对比](#3.3 完整示例对比)
- 四、实际应用示例
-
- [4.1 企业级应用架构示例](#4.1 企业级应用架构示例)
- [4.2 实际项目中的典型模式](#4.2 实际项目中的典型模式)
- [4.3 最佳实践总结](#4.3 最佳实践总结)
- 五、快速参考卡片
- 六、常见问题与陷阱
-
- [Q1: 为什么不能继承密封类?](#Q1: 为什么不能继承密封类?)
- [Q2: abstract和virtual的区别?](#Q2: abstract和virtual的区别?)
- [Q3: new和override的区别?](#Q3: new和override的区别?)
- [Q4: 为什么不能在类内部定义protected internal成员?](#Q4: 为什么不能在类内部定义protected internal成员?)
- 总结
一、访问修饰符详解
访问修饰符控制类型及其成员的可访问性,是封装特性的核心实现机制。
1.1 访问修饰符对比表
| 修饰符 | 访问级别 | 类内部 | 派生类 | 同一程序集 | 派生类(不同程序集) | 任何代码 |
|---|---|---|---|---|---|---|
public |
无限制 | ✅ | ✅ | ✅ | ✅ | ✅ |
private |
仅包含类 | ✅ | ❌ | ❌ | ❌ | ❌ |
protected |
派生类 | ✅ | ✅ | ❌ | ✅ | ❌ |
internal |
同一程序集 | ✅ | ✅ | ✅ | ❌ | ❌ |
protected internal |
并集 | ✅ | ✅ | ✅ | ✅ | ❌ |
private protected |
交集 | ✅ | ✅(同程序集) | ✅ | ❌ | ❌ |
1.2 访问修饰符详细说明
public - 公共访问
csharp
public class PublicClass
{
public string PublicField = "任何人都能访问";
public void PublicMethod()
{
Console.WriteLine("公共方法可以被任何代码调用");
}
}
// 任何地方都可以访问
var obj = new PublicClass();
obj.PublicField = "修改成功";
obj.PublicMethod(); // ✅ 可调用
private - 私有访问
csharp
public class PrivateDemo
{
private string _privateField = "只有本类能访问";
private void PrivateMethod() { }
public void PublicMethod()
{
// ✅ 类内部可以访问私有成员
_privateField = "修改成功";
PrivateMethod();
}
}
// ❌ 外部无法访问私有成员
var demo = new PrivateDemo();
// demo._privateField; // 编译错误
// demo.PrivateMethod(); // 编译错误
protected - 受保护访问
csharp
public class Animal
{
protected string _name = "动物";
protected void Eat() => Console.WriteLine("吃东西");
}
public class Dog : Animal
{
public void ShowName()
{
// ✅ 派生类可以访问protected成员
Console.WriteLine($"名字: {_name}");
Eat(); // ✅ 可调用
}
}
// ❌ 非派生类无法访问
var animal = new Animal();
// animal._name; // 编译错误
// animal.Eat(); // 编译错误
internal - 程序集内部访问
csharp
// Assembly1.dll
internal class InternalClass
{
internal string InternalField = "同程序集可访问";
}
public class PublicClass
{
internal void InternalMethod() { }
}
// Assembly2.dll (引用Assembly1)
// ❌ 无法访问Assembly1中的internal成员
// var obj = new InternalClass(); // 编译错误
protected internal - 受保护的内部访问
csharp
public class BaseClass
{
protected internal string ProInternal = "程序集内或派生类可访问";
}
// 同一程序集中的类
public class SameAssemblyClass
{
public void Test(BaseClass baseObj)
{
// ✅ 同程序集可以访问protected internal
Console.WriteLine(baseObj.ProInternal);
}
}
// 不同程序集中的派生类
public class DerivedInOtherAssembly : BaseClass
{
public void Test()
{
// ✅ 派生类也可以访问
Console.WriteLine(ProInternal);
}
}
private protected - 私有受保护访问 (C# 7.2+)
csharp
public class BaseClass
{
private protected string PrivateProtected = "同程序集的派生类可访问";
}
// 同一程序集的派生类
public class DerivedInSameAssembly : BaseClass
{
public void Test()
{
// ✅ 同程序集的派生类可访问
Console.WriteLine(PrivateProtected);
}
}
// 同一程序集的非派生类
public class NonDerivedClass
{
public void Test(BaseClass baseObj)
{
// ❌ 同程序集但不是派生类,无法访问
// Console.WriteLine(baseObj.PrivateProtected); // 编译错误
}
}
1.3 访问修饰符可视化对比
访问范围对比图:
public ┌─────────────────────────────────────┐
│ 任 何 代 码 │
└─────────────────────────────────────┘
protected ┌─────────────────────────────────────┐
internal │ 派生类 + 同一程序集 │
└─────────────────────────────────────┘
protected ┌─────────────────────────────────────┐
│ 派 生 类 │
└─────────────────────────────────────┘
internal ┌─────────────────────────────────────┐
│ 同 一 程 序 集 │
└─────────────────────────────────────┘
private ┌─────────────────────────────────────┐
protected │ 同一程序集的派生类 │
└─────────────────────────────────────┘
private ┌─────────────────────────────────────┐
│ 本 类 │
└─────────────────────────────────────┘
二、类修饰符详解
类修饰符控制类的行为和特性,包括继承、实例化、抽象性等。
2.1 类修饰符对比表
| 修饰符 | 作用 | 能修饰什么 | 说明 |
|---|---|---|---|
abstract |
抽象类 | 类、方法、属性 | 不能实例化,可包含抽象成员 |
sealed |
密封类 | 类、方法、属性 | 不能被继承 |
static |
静态类 | 类、方法、属性 | 不能实例化,只包含静态成员 |
partial |
部分类 | 类、结构、接口 | 类定义可分散在多个文件 |
new |
隐藏成员 | 类、方法、属性 | 显式隐藏基类成员 |
2.2 类修饰符详细说明
abstract - 抽象类
csharp
// 抽象类:不能实例化,可以包含抽象方法
public abstract class Shape
{
public string Color { get; set; }
// 抽象方法:没有实现,派生类必须重写
public abstract double GetArea();
// 虚方法:可以有默认实现,派生类可选重写
public virtual void Display()
{
Console.WriteLine($"形状颜色: {Color}");
}
// 具体方法:有完整实现
public void SetColor(string color)
{
Color = color;
}
}
// 派生类必须实现所有抽象成员
public class Circle : Shape
{
public double Radius { get; set; }
// 必须实现抽象方法
public override double GetArea()
{
return Math.PI * Radius * Radius;
}
// 可选重写虚方法
public override void Display()
{
base.Display();
Console.WriteLine($"圆形半径: {Radius}");
}
}
// 使用示例
// Shape shape = new Shape(); // ❌ 错误:不能实例化抽象类
Circle circle = new Circle { Radius = 5, Color = "红色" };
double area = circle.GetArea(); // ✅ 正确
sealed - 密封类
csharp
// 密封类:不能被继承
public sealed class ConfigurationManager
{
private static ConfigurationManager _instance;
private ConfigurationManager() { }
public static ConfigurationManager Instance
{
get
{
if (_instance == null)
_instance = new ConfigurationManager();
return _instance;
}
}
public string ConnectionString { get; set; }
}
// ❌ 错误:无法继承密封类
// public class ExtendedConfig : ConfigurationManager { }
// sealed 也可以修饰方法,防止派生类继续重写
public class BaseClass
{
public virtual void Method1() { }
public virtual void Method2() { }
}
public class DerivedClass : BaseClass
{
public sealed override void Method1() { } // 密封此方法
public override void Method2() { }
}
public class FurtherDerived : DerivedClass
{
// ❌ 错误:不能重写密封的方法
// public override void Method1() { }
// ✅ 正确:可以重写未密封的方法
public override void Method2() { }
}
static - 静态类
csharp
// 静态类:不能实例化,所有成员必须是静态的
public static class MathHelper
{
// 静态字段
public static double PI = 3.14159;
// 静态属性
public static double E { get; } = 2.71828;
// 静态方法
public static double CalculateCircleArea(double radius)
{
return PI * radius * radius;
}
// ❌ 错误:静态类不能包含实例成员
// public string Name { get; set; }
// public void InstanceMethod() { }
}
// 使用方式:直接通过类名调用
double area = MathHelper.CalculateCircleArea(10);
Console.WriteLine($"PI值: {MathHelper.PI}");
// MathHelper helper = new MathHelper(); // ❌ 错误:不能实例化静态类
partial - 部分类
csharp
// File: Person.cs
public partial class Person
{
private string _name;
private int _age;
public Person(string name, int age)
{
_name = name;
_age = age;
}
}
// File: Person.Methods.cs
public partial class Person
{
public void Display()
{
Console.WriteLine($"姓名: {_name}, 年龄: {_age}");
}
public void UpdateAge(int newAge)
{
_age = newAge;
}
}
// File: Person.Properties.cs
public partial class Person
{
public string Name
{
get => _name;
set => _name = value;
}
public int Age
{
get => _age;
set => _age = value;
}
}
// 所有部分会被编译成一个完整的类
var person = new Person("张三", 25);
person.Display(); // ✅ 来自 Person.Methods.cs
person.Name = "李四"; // ✅ 来自 Person.Properties.cs
new - 隐藏成员
csharp
public class BaseClass
{
public string Name = "基类名称";
public void Display() => Console.WriteLine("基类显示");
public virtual void Show() => Console.WriteLine("基类显示");
}
public class DerivedClass : BaseClass
{
// new: 显式隐藏基类成员(不使用多态)
public new string Name = "派生类名称";
public new void Display() => Console.WriteLine("派生类显示");
// override: 重写虚方法(使用多态)
public override void Show() => Console.WriteLine("派生类显示");
}
// 使用示例
BaseClass baseRef = new DerivedClass();
Console.WriteLine(baseRef.Name); // 输出: "基类名称" (使用基类字段)
baseRef.Display(); // 输出: "基类显示" (调用基类方法)
baseRef.Show(); // 输出: "派生类显示" (多态调用)
DerivedClass derivedRef = new DerivedClass();
Console.WriteLine(derivedRef.Name); // 输出: "派生类名称"
derivedRef.Display(); // 输出: "派生类显示"
2.3 修饰符组合规则
| 组合 | 是否允许 | 说明 |
|---|---|---|
abstract class |
✅ | 抽象类,可包含抽象成员 |
sealed class |
✅ | 密封类,不能被继承 |
static class |
✅ | 静态类,不能实例化 |
abstract sealed |
❌ | 矛盾:既抽象又密封 |
abstract static |
❌ | 矛盾:抽象类可以有实例成员 |
partial class |
✅ | 可与任何其他修饰符组合 |
三、综合对比分析
3.1 访问修饰符选择指南
| 场景 | 推荐修饰符 | 原因 |
|---|---|---|
| API接口 | public |
需要对外提供 |
| 内部实现细节 | private |
封装性最佳 |
| 需要派生类扩展 | protected |
平衡封装和扩展性 |
| 库内部共享 | internal |
对外隐藏实现 |
| 库内部+派生类扩展 | protected internal |
最大灵活性 |
| 同程序集派生类 | private protected |
最小暴露范围 |
3.2 类修饰符选择指南
| 设计意图 | 推荐修饰符 | 说明 |
|---|---|---|
| 定义通用契约 | abstract |
提供抽象基类 |
| 防止进一步继承 | sealed |
最终实现类 |
| 工具类 | static |
无状态工具方法 |
| 大型类拆分 | partial |
代码组织 |
| 隐藏基类成员 | new |
谨慎使用 |
3.3 完整示例对比
csharp
// 示例1: 不同访问级别的类成员
public class BankAccount
{
// public: 所有人都能看到余额
public decimal Balance { get; private set; }
// private: 账号是内部实现细节
private string _accountNumber;
// protected: 派生类可以修改利率
protected decimal InterestRate = 0.03m;
// internal: 同一程序集可以访问银行信息
internal string BankName = "MyBank";
// protected internal: 程序集内或派生类可访问
protected internal string BranchCode = "001";
// private protected: 仅同程序集的派生类可访问
private protected string ManagerName = "张三";
}
// 示例2: 不同类修饰符的类
public abstract class Vehicle // 抽象类
{
public abstract void Start();
}
public sealed class Car : Vehicle // 密封类,继承抽象类
{
public override void Start()
{
Console.WriteLine("汽车启动");
}
}
public static class VehicleFactory // 静态类
{
public static Car CreateCar() => new Car();
}
// 示例3: 部分类实现复杂功能
public partial class DataProcessor
{
private string _data;
public DataProcessor(string data)
{
_data = data;
}
}
public partial class DataProcessor
{
public string Process()
{
return _data.ToUpper();
}
public string Validate()
{
return string.IsNullOrEmpty(_data) ? "无效" : "有效";
}
}
四、实际应用示例
4.1 企业级应用架构示例
csharp
using System;
using System.Collections.Generic;
namespace EnterpriseApplication
{
#region 领域模型层 (Domain Layer)
// 抽象基类:定义领域实体的公共行为
public abstract class Entity
{
public int Id { get; protected set; }
public DateTime CreatedAt { get; private set; }
protected Entity()
{
CreatedAt = DateTime.Now;
}
// 抽象方法:派生类必须实现业务规则
public abstract bool IsValid();
// 虚方法:提供默认实现,派生类可选重写
public virtual string GetDisplayName() => $"Entity-{Id}";
}
// 具体实体类
public sealed class Product : Entity // sealed: 不允许进一步继承
{
public string Name { get; private set; }
public decimal Price { get; private set; }
// 构造函数:internal确保只能在程序集内创建
internal Product(string name, decimal price)
{
Name = name;
Price = price;
}
public override bool IsValid()
{
return !string.IsNullOrEmpty(Name) && Price > 0;
}
public override string GetDisplayName() => Name;
// 业务方法
public void UpdatePrice(decimal newPrice)
{
if (newPrice <= 0)
throw new ArgumentException("价格必须大于0");
Price = newPrice;
}
}
#endregion
#region 仓储层 (Repository Layer)
// 接口:定义契约
public interface IRepository<T> where T : Entity
{
T GetById(int id);
IEnumerable<T> GetAll();
void Add(T entity);
void Update(T entity);
void Delete(int id);
}
// 抽象基类:提供通用实现
public abstract class BaseRepository<T> : IRepository<T>
where T : Entity
{
protected readonly List<T> _entities = new List<T>();
public virtual T GetById(int id) =>
_entities.Find(e => e.Id == id);
public virtual IEnumerable<T> GetAll() => _entities;
public abstract void Add(T entity);
public virtual void Update(T entity) { }
public virtual void Delete(int id) { }
}
// 具体实现:internal隐藏实现细节
internal class ProductRepository : BaseRepository<Product>
{
private static int _nextId = 1;
public override void Add(Product entity)
{
// 使用反射设置Id(实际项目中应使用更好的方式)
typeof(Entity).GetProperty("Id")?
.SetValue(entity, _nextId++);
_entities.Add(entity);
}
// protected internal: 程序集内的其他类可以访问此方法
protected internal void ClearAll() => _entities.Clear();
// private protected: 仅同程序集的派生类可访问
private protected void LogOperation(string operation)
{
Console.WriteLine($"[日志] {operation} 操作执行");
}
}
#endregion
#region 服务层 (Service Layer)
// 静态工具类:提供扩展方法
public static class RepositoryExtensions
{
public static bool Exists<T>(this IRepository<T> repository, int id)
where T : Entity
{
return repository.GetById(id) != null;
}
}
// 业务服务类
public class ProductService
{
private readonly IRepository<Product> _repository;
public ProductService(IRepository<Product> repository)
{
_repository = repository;
}
public void CreateProduct(string name, decimal price)
{
var product = new Product(name, price);
if (!product.IsValid())
throw new InvalidOperationException("产品信息无效");
_repository.Add(product);
Console.WriteLine($"产品 {name} 创建成功");
}
public void DisplayAllProducts()
{
var products = _repository.GetAll();
Console.WriteLine("\n产品列表:");
foreach (var product in products)
{
Console.WriteLine($" {product.GetDisplayName()} - {product.Price:C}");
}
}
}
#endregion
#region 表示层 (Presentation Layer)
// 部分类:WinForm设计器生成的代码和业务逻辑分离
public partial class ProductForm : Form
{
private readonly ProductService _service;
public ProductForm()
{
InitializeComponent(); // 在另一个部分类文件中
_service = new ProductService(new ProductRepository());
}
private void btnCreate_Click(object sender, EventArgs e)
{
try
{
_service.CreateProduct(txtName.Text, decimal.Parse(txtPrice.Text));
_service.DisplayAllProducts();
}
catch (Exception ex)
{
MessageBox.Show($"错误: {ex.Message}");
}
}
}
// 部分类的另一部分(通常由设计器生成)
public partial class ProductForm
{
private System.Windows.Forms.TextBox txtName;
private System.Windows.Forms.TextBox txtPrice;
private System.Windows.Forms.Button btnCreate;
private void InitializeComponent()
{
txtName = new TextBox();
txtPrice = new TextBox();
btnCreate = new Button();
// 控件初始化代码...
}
}
#endregion
#region 测试类
class Program
{
static void Main(string[] args)
{
// 演示访问修饰符效果
var repository = new ProductRepository(); // 同一程序集可访问internal类
var service = new ProductService(repository);
service.CreateProduct("笔记本电脑", 5999.99m);
service.CreateProduct("鼠标", 99.99m);
service.DisplayAllProducts();
// 演示static类使用
var exists = repository.Exists(1); // 使用扩展方法
Console.WriteLine($"\n产品1存在: {exists}");
}
}
#endregion
}
4.2 实际项目中的典型模式
csharp
// 1. 单例模式 - 使用sealed和private
public sealed class Configuration
{
private static readonly Configuration _instance = new Configuration();
private Configuration() { } // private构造函数防止外部实例化
public static Configuration Instance => _instance;
public string ConnectionString { get; set; }
}
// 2. 工厂模式 - 使用internal和public
public interface ILogger
{
void Log(string message);
}
internal class FileLogger : ILogger // internal隐藏实现
{
public void Log(string message) => File.AppendAllText("log.txt", message);
}
internal class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine(message);
}
public static class LoggerFactory // public静态工厂
{
public static ILogger CreateFileLogger() => new FileLogger();
public static ILogger CreateConsoleLogger() => new ConsoleLogger();
}
// 3. 模板方法模式 - 使用abstract和protected
public abstract class DataExporter
{
// 模板方法
public void Export()
{
Connect();
ExtractData();
TransformData();
WriteOutput();
Disconnect();
}
// 公共方法,所有派生类共享
private void Connect() => Console.WriteLine("连接数据库");
private void Disconnect() => Console.WriteLine("断开连接");
// 派生类必须实现的抽象方法
protected abstract void ExtractData();
protected abstract void TransformData();
protected abstract void WriteOutput();
}
public class ExcelExporter : DataExporter
{
protected override void ExtractData() => Console.WriteLine("提取Excel数据");
protected override void TransformData() => Console.WriteLine("转换Excel格式");
protected override void WriteOutput() => Console.WriteLine("写入Excel文件");
}
// 4. 扩展方法 - 使用static类
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string str) => string.IsNullOrEmpty(str);
public static string ToTitleCase(this string str) =>
str.Substring(0, 1).ToUpper() + str.Substring(1).ToLower();
}
4.3 最佳实践总结
csharp
public class BestPracticesDemo
{
// ✅ 好的实践:最小权限原则
private string _privateField; // 尽可能使用private
protected string _protectedField; // 需要派生类访问时使用protected
internal string _internalField; // 同一程序集共享时使用internal
// ✅ 好的实践:属性暴露,字段隐藏
public string PublicProperty { get; private set; }
// ✅ 好的实践:只读属性
public DateTime CreatedTime { get; } = DateTime.Now;
// ✅ 好的实践:抽象类定义契约
public abstract class Validator
{
public abstract bool Validate(object obj);
}
// ✅ 好的实践:密封类防止滥用继承
public sealed class ConcreteValidator : Validator
{
public override bool Validate(object obj) => obj != null;
}
// ❌ 不好的实践:过度使用public
public string BadField; // 应该用属性替代
// ❌ 不好的实践:不必要的internal
private void ShouldBePrivate() { } // internal不必要
// ❌ 不好的实践:滥用new隐藏成员
public class BadDerived : BaseClass
{
public new void Method() { } // 容易引起混淆
}
}
五、快速参考卡片
访问修饰符速查
┌─────────────────────────────────────────────────────────────┐
│ 访问修饰符决策树 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 需要对外部程序集公开? │
│ ├─ 是 → public │
│ └─ 否 → 继续 │
│ ↓ │
│ 需要派生类访问? │
│ ├─ 是 → 继续 │
│ │ ↓ │
│ │ 需要同一程序集访问? │
│ │ ├─ 是 → protected internal │
│ │ └─ 否 → protected │
│ └─ 否 → 继续 │
│ ↓ │
│ 需要同一程序集访问? │
│ ├─ 是 → 继续 │
│ │ ↓ │
│ │ 需要派生类访问? │
│ │ ├─ 是 → private protected │
│ │ └─ 否 → internal │
│ └─ 否 → private │
│ │
└─────────────────────────────────────────────────────────────┘
类修饰符速查
┌─────────────────────────────────────────────────────────────┐
│ 类修饰符决策树 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 是否需要实例化? │
│ ├─ 否 → static │
│ └─ 是 → 继续 │
│ ↓ │
│ 是否包含未实现成员? │
│ ├─ 是 → abstract │
│ └─ 否 → 继续 │
│ ↓ │
│ 是否允许被继承? │
│ ├─ 否 → sealed │
│ └─ 是 → 普通类 │
│ │
│ 是否需要拆分到多个文件? │
│ └─ 是 → partial │
│ │
└─────────────────────────────────────────────────────────────┘
六、常见问题与陷阱
Q1: 为什么不能继承密封类?
A: 密封类表示这是最终实现,防止进一步扩展。通常用于:
- 安全敏感的类(如密码处理)
- 性能关键的类(避免虚方法调用)
- 单例模式实现
Q2: abstract和virtual的区别?
csharp
public abstract class Example
{
// abstract: 必须由派生类实现,不能有方法体
public abstract void MustImplement();
// virtual: 提供默认实现,派生类可选重写
public virtual void CanOverride()
{
Console.WriteLine("默认实现");
}
}
Q3: new和override的区别?
csharp
public class Base { public virtual void Method() { } }
public class Derived : Base
{
// new: 隐藏基类方法(编译时绑定)
public new void Method() { }
// override: 重写基类方法(运行时绑定)
public override void Method() { }
}
Q4: 为什么不能在类内部定义protected internal成员?
A : 可以定义,protected internal 是有效的组合,表示"程序集内或派生类可访问"。
总结
- 访问修饰符 控制可见性,遵循最小权限原则
- 类修饰符控制行为特性,合理选择可提高代码质量
- 组合使用修饰符可以实现更精细的控制
- 实际项目中根据架构分层选择合适的修饰符组合
掌握这些修饰符的正确使用,是编写高质量、可维护C#代码的基础。