VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • 封装:访问修饰符与属性(Property)

封装:访问修饰符与属性(Property)

  1. 封装的核心概念
    封装是面向对象编程(OOP)三大特性之一,核心是隐藏类的内部实vb.net教程C#教程python教程SQL教程access 2010教程现细节,仅对外暴露可控的访问接口。通过封装,我们可以保护类的内部数据不被非法修改,同时降低代码的耦合度,提高可维护性。
    封装的实现依赖两个关键工具:访问修饰符(控制成员可见性)和属性(封装字段的访问)。
  2. 访问修饰符:控制成员的可见性
    访问修饰符用于定义类或类成员(字段、属性、方法等)的可访问范围,是封装的基础。C#中常用的访问修饰符有以下6种:
    修饰符 可见范围
    public 所有类均可访问(对外完全暴露)
    private 仅当前类内部可访问(完全隐藏,默认修饰符)
    protected 当前类及子类可访问
    internal 同一程序集(Assembly)内的类可访问
    protected internal 同一程序集内的类或当前类的子类可访问(组合protected和internal)
    private protected 同一程序集内的当前类的子类可访问(C#7.2+新增,更严格的protected)
    实例:访问修饰符的使用
    csharp
	public class Person
	{
	// private字段:仅类内部可访问
	private string _name; 
	private int _age;
	
	// protected方法:当前类及子类可访问
	protected void ValidateAge(int age)
	{
	if (age < 0 || age > 120)
	throw new ArgumentOutOfRangeException(nameof(age), "年龄必须在0-120之间");
	}
	
	// public方法:所有类可访问
	public void SetInfo(string name, int age)
	{
	_name = name;
	ValidateAge(age); // 调用protected方法
	_age = age;
	}
	
	// internal属性:同一程序集内可访问
	internal string GetName()
	{
	return _name;
	}
	}

逐行讲解:
private string _name:_name是私有字段,外部无法直接修改,只能通过类内部的方法(如SetInfo)间接访问。
protected void ValidateAge:ValidateAge是保护方法,只能在Person类或其子类中调用,用于验证年龄合法性。
public void SetInfo:SetInfo是公共方法,对外暴露的接口,外部通过它设置_name和_age,同时内部会调用ValidateAge确保数据合法。
internal string GetName:GetName是内部方法,同一程序集内的其他类可以调用它获取_name的值。
3. 属性(Property):封装字段的可控接口
属性是封装字段的常用方式,它通过get(读取)和set(写入)方法控制对字段的访问,相比直接使用public字段,属性可以添加验证逻辑、日志记录等额外操作,确保数据安全。
3.1 字段 vs 属性:为什么不用public字段?
直接使用public字段会导致外部可以随意修改数据,无法保证合法性。例如:
csharp

	// 错误示例:public字段无保护
	public class Person
	{
	public int Age; // 外部可直接赋值Age=-10,导致数据非法
	}

而属性通过set方法可以添加验证逻辑,避免非法数据:
csharp

	// 正确示例:用属性封装字段
	public class Person
	{
	private int _age; // 私有字段,隐藏内部存储
	
	// 公共属性:对外暴露可控访问
	public int Age
	{
	get { return _age; } // 读取字段值
	set 
	{ 
	// 添加验证逻辑
	if (value >=0 && value <=120) 
	_age = value; 
	else 
	throw new ArgumentOutOfRangeException(nameof(value), "年龄必须在0-120之间"); 
	}
	}
	}

3.2 属性的语法与类型
属性的基本语法由get和set访问器组成,根据访问器的有无,可分为以下类型:
(1)读写属性(Full Property)
同时包含get和set访问器,允许读写操作:
csharp

	public class Person
	{
	private string _name;
	
	public string Name
	{
	get { return _name; } // 读取
	set { _name = value; } // 写入(value是关键字,代表外部传入的值)
	}
	}

(2)只读属性
仅包含get访问器,不允许外部写入:
csharp

	public class Person
	{
	private DateTime _birthDate;
	
	// 只读属性:计算年龄(无法直接修改)
	public int Age
	{
	get 
	{ 
	return DateTime.Now.Year - _birthDate.Year; 
	}
	}
	
	// 构造函数:初始化生日
	public Person(DateTime birthDate)
	{
	_birthDate = birthDate;
	}
	}

(3)只写属性
仅包含set访问器(很少使用,通常用于敏感数据的写入):
csharp

	public class Person
	{
	private string _password;
	
	// 只写属性:仅允许设置密码,不允许读取
	public string Password
	{
	set { _password = HashPassword(value); } // 写入时加密
	}
	
	private string HashPassword(string password)
	{
	// 模拟加密逻辑
	return password.GetHashCode().ToString();
	}
	}

(4)自动实现的属性(Auto-Implemented Property)
对于简单的属性(无额外逻辑),可以省略显式字段,由编译器自动生成隐藏字段(称为“后备字段”):
csharp

	public class Person
	{
	// 自动属性:编译器自动生成private string _name;
	public string Name { get; set; } 
	
	// 只读自动属性(C#6+):仅允许构造函数或初始化器赋值
	public DateTime BirthDate { get; } 
	
	// 构造函数初始化只读属性
	public Person(DateTime birthDate)
	{
	BirthDate = birthDate;
	}
	}

(5)Init-Only属性(C#9+)
init访问器允许在对象初始化时赋值,之后无法修改(用于不可变对象):
csharp

	public class Person
	{
	// Init-only属性:仅在初始化时赋值
	public string Name { get; init; } 
	public int Age { get; init; }
	}
	
	// 使用:初始化器赋值
	var person = new Person { Name = "张三", Age = 25 }; 
	// person.Name = "李四"; // 编译错误:init属性无法后续修改
  1. 封装的实践:完整类实例
    以下是一个结合访问修饰符和属性的完整实例,展示封装的最佳实践:
    csharp
	using System;
	
	public class Person
	{
	// 私有字段:隐藏内部数据
	private string _name;
	private int _age;
	private string _email;
	
	// 静态私有字段:类级别的常量(所有实例共享)
	private static readonly string _defaultEmailSuffix = "@example.com";
	
	// 构造函数:初始化必填字段
	public Person(string name, int age)
	{
	_name = name;
	Age = age; // 调用属性的set方法,验证年龄
	_email = $"{name.ToLower()}{_defaultEmailSuffix}"; // 自动生成邮箱
	}
	
	// 公共属性:封装_name(读写)
	public string Name
	{
	get { return _name; }
	set 
	{ 
	if (!string.IsNullOrEmpty(value)) 
	_name = value; 
	else 
	throw new ArgumentNullException(nameof(value), "姓名不能为空"); 
	}
	}
	
	// 公共属性:封装_age(读写+验证)
	public int Age
	{
	get { return _age; }
	set 
	{ 
	if (value >=0 && value <=120) 
	_age = value; 
	else 
	throw new ArgumentOutOfRangeException(nameof(value), "年龄必须在0-120之间"); 
	}
	}
	
	// 只读属性:封装_email(仅读取)
	public string Email
	{
	get { return _email; }
	}
	
	// 公共方法:对外暴露的行为
	public void Introduce()
	{
	Console.WriteLine($"我是{_name},今年{_age}岁,邮箱是{_email}");
	}
	}
	
	// 使用示例
	class Program
	{
	static void Main()
	{
	try
	{
	var person = new Person("张三", 25);
	person.Introduce(); // 输出:我是张三,今年25岁,邮箱是zhangsan@example.com
	
	person.Age = 30; // 修改年龄(合法)
	person.Name = "李四"; // 修改姓名(合法)
	// person.Email = "lisi@example.com"; // 编译错误:只读属性无法修改
	
	person.Age = 150; // 抛出异常:年龄超出范围
	}
	catch (Exception ex)
	{
	Console.WriteLine(ex.Message);
	}
	}
	}

逐行讲解:
private string _name:私有字段,隐藏内部存储,外部无法直接访问。
static readonly string _defaultEmailSuffix:静态只读字段,类级别的常量,所有实例共享。
public Person(string name, int age):构造函数,初始化必填字段,调用Age属性验证年龄。
public string Name:公共属性,通过set方法验证姓名非空。
public int Age:公共属性,通过set方法验证年龄范围。
public string Email:只读属性,仅允许读取自动生成的邮箱地址。
public void Introduce:公共方法,对外暴露的行为,内部使用私有字段。
5. 基础知识拓展
5.1 封装的好处
数据安全:通过属性验证逻辑,避免非法数据输入。
代码可维护性:修改内部实现(如字段名)时,只需调整属性的get/set方法,外部代码无需改动。
降低耦合度:外部仅依赖属性接口,不依赖内部实现细节。
5.2 自动属性的背后原理
自动实现的属性(如public string Name { get; set; })看似没有字段,实则编译器会自动生成一个后备字段(Backing Field),例如:
csharp

	// 编译器自动生成的后备字段
	private string <Name>k__BackingField;
	
	// 自动属性的实际实现
	public string Name
	{
	get { return <Name>k__BackingField; }
	set { <Name>k__BackingField = value; }
	}

5.3 接口中的属性
接口中可以定义属性,但只能声明访问器,不能包含实现:
csharp

	public interface IPerson
	{
	string Name { get; set; } // 接口属性:仅声明
	int Age { get; } // 只读接口属性
	}
	
	// 实现接口
	public class Student : IPerson
	{
	public string Name { get; set; }
	public int Age { get; set; }
	}
  1. 总结
    封装是OOP的核心特性,通过访问修饰符控制成员可见性,通过属性封装字段的访问,实现了“隐藏细节、暴露接口”的目标。合理使用封装可以让代码更健壮、易维护,是编写高质量C#程序的基础。
    关键要点:
    优先使用private字段隐藏内部数据,通过public属性对外暴露可控访问。
    属性的set方法应添加验证逻辑,确保数据合法性。
    避免使用public字段,除非是常量(const或readonly)。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49364.html

相关教程