资讯动态

C#基础2- 面向对象中级应用2—看了不亏系列

发布时间:2026/10/21 11:36:13 来源:尧图企业网站定制
1.类简介什么是类和对象。理解面向对象编程的基本概念知识点类Class模板/抽象模型描述一类事物共同的特征数据与行为操作是类型定义本身不占用实例内存。对象Object /实例Instance类的实例基于模板创建出来的具体实体拥有独立的数据内存。面向对象三大基础思想封装、继承、多态。面向对象业务价值对业务实体建模代码高内聚便于业务迭代、团队协作、单元测试。业务系统中类一般对应领域实体如会员、订单、商品、账户而不是零散函数。举例会员领域模型只建模概念不写完整语法业务场景电商会员。Member类代表所有会员的模板包含特征会员 ID、昵称、余额、会员等级行为签到、扣减余额、升级。对象 1memberA实例数据ID1001昵称 张三余额 2000等级 黄金会员对象 2memberB实例数据ID1002昵称 李四余额 500等级 普通会员Member是类图纸memberA、memberB是两个独立对象按照图纸造出的两个实体各自数据互不干扰语法设计注意点类是引用类型类本身不能直接赋值使用必须实例化为对象。不要把无关业务属性 / 方法塞进同一个类遵循单一职责企业编码规范。区分类型和实例Member是类型new Member()才是对象实例。面向对象不是 “必须用类”简单工具逻辑不要强行封装类避免过度设计。2.定义类与创建对象(new)。掌握类的定义语法学会使用new关键字创建对象知识点类定义语法访问修饰符 class 类名 { }企业默认业务实体用public class。new关键字两件核心工作①在堆上分配实例内存②调用构造函数完成对象初始化返回对象引用。引用变量栈中存储对象引用地址堆存放对象真实数据多个变量可指向同一个对象。对象销毁C# 由 GC 垃圾回收自动回收堆内存无需手动 free。举例客户账户实体多变量引用同一个对象C#//客户账户类public class CustomerAccount{public long AccountId;public string CustomerName;public decimal Balance;}class Program{static void Main(){// new 创建堆上对象acc1持有引用CustomerAccount acc1 new CustomerAccount();acc1.AccountId 8001;acc1.CustomerName 王海涛;acc1.Balance 15600.25m;// acc2和 acc1 指向堆里**同一个对象**不是复制一份CustomerAccount acc2 acc1;acc2.Balance - 2000;Console.WriteLine(acc1.Balance); //输出13600.25会同步变化}}CustomerAccount acc2 acc1;为什么改 acc2acc1 跟着变CustomerAccount是class引用类型。内存模型CustomerAccount acc1 new CustomerAccount();new在堆Heap开辟一块内存存放AccountId、CustomerName、Balance这些数据栈 Stack 上的变量acc1不存对象本身只存堆对象的地址引用。Plain Text栈acc1 → 0x100堆地址堆 0x100AccountId8001, Name王海涛, Balance15600.25CustomerAccount acc2 acc1;这行是拷贝引用地址不是复制整个对象把 acc1 里面保存的堆地址复制一份给 acc2Plain Text栈acc1 → 0x100栈acc2 → 0x100 ← 和acc1指向堆里**同一块内存**acc2.Balance - 2000;通过 acc2 的地址找到堆 0x100 这块对象修改堆里面的 Balanceacc1 还是指向同一个堆对象读取的时候自然拿到修改后的值一句话总结两个变量只是两个门牌号房子只有一间随便哪个门牌号进去改房子里的数据所有人看到的房子内容都会变。对比如果你想复制一份全新独立对象需要手动克隆而不是直接赋值C#//真正新建独立对象CustomerAccount acc2 new CustomerAccount();acc2.AccountId acc1.AccountId;acc2.CustomerName acc1.CustomerName;acc2.Balance acc1.Balance;语法设计注意点new只对引用类型class分配堆内存值类型(struct) new不会进堆。引用赋值acc2 acc1是引用拷贝不是对象克隆修改任意变量会互相影响。类名推荐 Pascal 大驼峰企业 C# 强制规范不能数字开头。未new的类变量默认值为null直接访问成员会抛出NullReferenceException空引用异常业务系统高频bug。new后不接收引用new CustomerAccount();匿名对象创建完直接丢弃GC 很快回收一般极少用。3.字段声明与默认值。深入理解字段的声明语法、默认值规则和初始化方式知识点字段Field类内部的私有 / 公共变量存储对象的原始数据属于实例状态。C# 字段默认值规则所有字段会自动赋默认值不会像局部变量一样要求手动初始化。数值类型0 / 0.0mbool:false引用类型null。实例字段每个对象独立一份静态字段属于类本身所有对象共享同一份。字段初始化时机①声明时直接赋值②构造函数内赋值。规范业务实体尽量不直接公开public字段优先用属性封装public 字段只用于简单 POCO 模型临时场景。案例订单实体区分实例字段、静态字段、默认值表现C#public class Order{//实例字段每个订单对象各自独立public long OrderId;public string OrderTitle;public decimal OrderAmount;public bool IsPaid;// 静态字段属于Order类所有订单共享记录系统订单总计数public static long TotalOrderCount;// 声明时直接初始化字段public DateTime CreateTime DateTime.Now;}class Program{static void Main(){Order order1 new Order();Order order2 new Order();Console.WriteLine(order1.OrderId); // long默认0Console.WriteLine(order1.OrderTitle); // string引用类型默认nullConsole.WriteLine(order1.IsPaid); // bool默认falseOrder.TotalOrderCount;Console.WriteLine(Order.TotalOrderCount); //静态字段order1/order2共用}}静态字段Order.TotalOrderCount为什么所有实例共用实例字段 vs 静态字段 核心区别实例字段普通字段写在类里面不带static属于对象实例每new Order()一次堆上就新建一套独立实例字段。order1 有自己的 OrderId、CreateTimeorder2 有另一套独立的。Plain Text堆order1实例OrderId0, CreateTimexxx堆order2实例OrderId0, CreateTimexxx静态字段加了static不属于任何对象实例属于Order这个类本身。内存位置不在实例堆里面在【静态存储区】。整个程序生命周期全局只有这一份内存不管new多少个Order对象全部共享这一个变量。Plain Text静态存储区Order.TotalOrderCount 0;order1、order2、order999 全部访问同一个静态存储区变量Plain TextOrder.TotalOrderCount;不管你通过order1.TotalOrderCount还是Order.TotalOrderCount访问的都是静态区同一个变量。企业规范推荐写Order.TotalOrderCount不要用实例对象访问静态成员编译器会警告可读性差。语法设计注意点局部变量没有默认值字段才有默认值不要混淆。静态字段属于类通过类名.字段访问实例字段通过对象.字段访问。不推荐大量public公开字段破坏封装外部可随意篡改内部原始数据。字段声明初始化代码执行在构造函数代码之前。静态字段全局唯一多线程并发场景直接读写静态字段会产生线程安全问题。4.属性:get、set与自动属性。掌握属性的定义和使用理解封装的重要性知识点属性本质字段的访问器包装器get读set写不是字段是方法的语法糖。完整属性后端私有字段 get/set自动属性编译器自动生成隐藏私有后备字段简化代码。访问修饰控制get/set可单独设置权限public get; private set;只读属性外部只能读不能写。封装价值在 get/set 中加入数据校验、日志、数据格式化、触发业务事件控制外部对内部状态的修改避免脏数据。案例会员余额属性set中加入业务校验防止负数余额C#public class VipMember{//完整属性私有后备字段private decimal _balance;public decimal Balance{get{// 读取时可以做日志、脱敏Console.WriteLine($读取余额时间{DateTime.Now});return _balance;}set{// 写入业务校验余额不能小于0if (value 0){throw new ArgumentOutOfRangeException(nameof(value), 账户余额不能为负数);}_balance value;}}// 自动属性编译器自动生成隐藏字段public long MemberId { get; set; }// 只读自动属性外部仅可读仅类内部可赋值public string MemberName { get; private set; }public VipMember(string name){MemberName name;}}class Program{static void Main(){VipMember member new VipMember(周明);member.MemberId 10005;member.Balance 8500m;member.Balance -100; // 抛出异常拦截非法负数余额}}语法设计注意点set内部使用关键字value代表外部传入赋值的值是内置关键字不能改名。自动属性不能直接写校验逻辑校验逻辑需要完整属性 私有后备字段。public get;private set是企业实体最常用写法外部只读只能在构造函数 / 类内部修改。属性不能传参和方法区分属性用于状态读写方法用于执行动作。不要在get里写复杂耗时业务逻辑数据库查询、网络请求容易被误多次调用造成性能隐患。5.方法无参有参与返回值。掌握方法的定义和调用理解类行为的设计知识点类的方法描述对象行为属于实例动作语法访问修饰符 返回类型 方法名(参数列表){}。无参方法不需要传入参数有参方法接收输入返回值方法用return输出结果void无返回值。参数分为值参数默认值拷贝、后续可扩展 ref/out/in业务方法尽量做参数合法性校验。面向对象设计方法属于对象行为操作当前对象自身属性 / 字段。案例购物车实体包含多个业务方法新增商品、计算总价、清空购物车C#public class Customer{public long Id { get; }public string Name { get; }public string Phone { get; }public DateTime RegisterTime { get; }//无参构造public Customer(){RegisterTime DateTime.Now;}// 带参构造1ID姓名public Customer(long id, string name) : this() // 调用无参构造{Id id;Name name;}// 带参构造2完整必填参数public Customer(long id, string name, string phone): this(id, name){if (string.IsNullOrWhiteSpace(phone))throw new ArgumentException(手机号不能为空);Phone phone;}}class Program{static void Main(){Customer c1 new Customer();Customer c2 new Customer(2001, 赵亮);Customer c3 new Customer(2002, 陈雪, 13800138000);}}语法设计注意点手写任何构造函数编译器自动删除默认无参构造序列化框架EF、Json需要无参构造时要手动保留。为什么需要无参构造EF、Newtonsoft.Json、System.Text.Json 这类框架反射创建对象实例的时候默认优先找无参数构造函数。原理框架通过Activator.CreateInstance(类型)这个方法默认调用无参构造。 如果你只写了带参构造C# 编译器不会自动生成默认无参构造反射创建实例直接报错。手动保留无参构造的几种写法C#public class UserAccount{//带参数构造业务代码自己用public UserAccount(decimal initAmount){_availableAmount initAmount;}// ✅ 手动保留【无参构造】给EF/序列化框架使用// 可以加访问修饰符public / protected 都可以// EF 支持 protected不需要公开给外部业务调用推荐protected UserAccount(){// 框架反射实例化时走这个构造}private decimal _availableAmount;}只要你写了任意带参构造编译器就删掉默认无参构造必须手写出来给 EF/Json 序列化反射用。构造函数不能写 return 返回值不能手动主动调用构造函数仅new的时候自动执行。this()调用其他构造必须写在构造函数第一行。构造函数适合初始化必填属性、参数校验不要在构造写复杂业务逻辑数据库、网络请求容易引发异常导致对象处于半初始化状态。只读属性/仅get的自动属性只能在构造函数内赋值构造执行完毕无法修改适合业务只读主键。6.this关键字区分字段与参数。掌握this关键字的使用理解如何区分字段和参数知识点this关键字代表当前实例对象本身。核心用途 1局部参数/方法参数和实例字段重名时用this.字段名区分命名冲突最常用场景。用途 2在构造函数中this(参数)调用同类其他构造函数。用途 3把当前对象作为参数传递给其他方法。this只能在实例方法/实例构造函数中使用静态方法中不能使用this静态不属于实例。案例账户修改信息方法参数名与字段同名使用this区分C#public class UserAccount{privatestring _userName;private decimal _availableAmount;//构造函数参数名和字段同名this区分public UserAccount(string userName, decimal availableAmount){this._userName userName;this._availableAmount availableAmount;}// 修改账户名称参数名和字段名一致this区分实例字段public void SetUserName(string userName){if (string.IsNullOrWhiteSpace(userName))throw new ArgumentException(用户名不能为空);this._userName userName;}/// summary/// 转账方法把当前对象this传给外部校验方法/// /summary/// param nameamount转账金额/param/// param nametargetAccount收款目标账户/param/// returns转账成功返回true失败返回false/returnspublic bool Transfer(decimal amount, UserAccount targetAccount){// 调用外部校验类传入【当前账户对象this】做账户合法性校验if (!AccountValidator.CheckAccount(this))return false;// 判断当前账户可用余额 是否小于转账金额余额不足则转账失败if (this._availableAmount amount) return false;// 扣减当前账户余额this._availableAmount - amount;// 给目标收款账户增加对应金额targetAccount._availableAmount amount;return true;}}// 外部校验类public static class AccountValidator{public static bool CheckAccount(UserAccount account){return account ! null;}}class Program{static void Main(){var u1 new UserAccount(用户A, 10000m);var u2 new UserAccount(用户B, 2000m);u1.Transfer(1500, u2);}}语法设计注意点当参数名和字段名字不一样时this.可以省略企业代码中部分团队规范会显式写this提升可读性。this指向当前实例静态方法没有实例静态方法内写this直接编译报错。不要滥用 this不要每个成员都强制加 this造成代码冗余。在构造函数this(...)调用其他构造必须写在第一行后面不能再写其他代码。this 只能代表当前实例不能用来访问静态成员静态成员直接类名访问。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑