1.什么是C#,它的主要特点是什么?
2.C#中struct和class的区别是什么?
3.什么是 finally 块?它的作用?有 return 语句它还会执行吗?
定义:finally 是 C# 异常处理(try-catch-finally)的组成部分,无论 try 块是否抛出异常、catch 块是否执行,finally 块都会执行(除非进程被强制终止)。
作用:核心用于释放非托管资源(如文件流、数据库连接、网络连接等),避免资源泄漏;也可执行必须兜底的逻辑(如关闭句柄、释放锁)。
return 语句场景:会执行。finally 块在 return 语句执行后、方法返回前触发,若 finally 块内也有 return,会覆盖 try/catch 中的 return 值(不推荐此写法)。
4.什么是属性(Property)?与字段(Field)相比有什么区别?
5.C#中的readonly和const有什么区别?
6.什么是多播委托?以及它的具体用法
定义:多播委托是 C# 委托的特性,指一个委托实例可包含多个方法引用(形成委托链),调用委托时会按顺序执行链中所有方法;仅支持返回值为 void 的委托(有返回值时仅保留最后一个方法的返回值)。
具体用法:
声明符合要求的委托类型;
通过
+=向委托实例添加方法引用,通过-=移除方法引用;调用委托实例,自动按顺序执行委托链中的所有方法。
典型场景:C# 事件(底层封装多播委托)、多模块回调链(如状态变更时批量触发多个回调方法)。
7.什么是锁?如何在 C# 实现锁机制
定义:锁是多线程编程中解决资源竞争的核心机制,用于保证临界区代码(同一时间仅能被一个线程执行的代码)的原子性,避免多线程操作共享资源导致的数据错乱。
C# 实现方式:
lock关键字:最常用,是Monitor类的语法糖,自动封装 Monitor.Enter/Exit,需锁定私有静态只读对象(避免锁 this、值类型、字符串字面量);Monitor类:手动控制锁,可实现超时获取锁、尝试获取锁等精细操作,需在 finally 中释放锁避免泄漏;
Mutex:支持跨进程锁,适用于多应用程序访问同一共享资源的场景,需手动释放并处理资源释放;
其他类型:SpinLock(自旋锁,适合临界区执行极短的场景)、Semaphore/SemaphoreSlim(控制并发线程数)、ReaderWriterLockSlim(读写锁,优化读多写少场景性能)。
核心原则:锁粒度最小化、避免锁嵌套(防死锁)、优先使用 lock(简单安全),特殊场景用 Monitor/Mutex 等。
8.什么是索引器?如何定义和使用?
索引器定义:索引器是允许类 / 结构体实例像数组一样通过 “下标” 访问内部数据的特殊成员,本质是绑定到
this关键字的属性,核心用于封装集合类 / 自定义容器的下标访问逻辑。定义方式:以
this作为 “名称”,指定索引参数(如int类型下标),搭配get/set访问器;支持多参数索引(如二维下标)和索引器重载(不同参数类型的索引器)。使用方式:通过 “实例名 [索引值]” 的形式读写数据,语法与数组访问一致;可结合访问器控制读写权限(如只读索引器)。
9.什么是多态?它有哪些形式?
多态定义:多态是面向对象三大核心特性之一,指 “同一操作作用于不同对象时,表现出不同的行为”,核心是 “一个接口,多种实现”,旨在降低代码耦合、提高扩展性。
主要形式:
编译时多态(静态多态):在编译阶段确定调用逻辑,通过方法重载(同一类中同名不同参数的方法)、运算符重载实现。
运行时多态(动态多态):在运行阶段根据对象的实际类型确定调用逻辑,通过 “继承 + 虚方法(
virtual/override)”、抽象类(abstract)、接口(interface)实现。
10.ArrayList 和 List 区别?为什么更推荐使用泛型集合如 List<T>?
核心区别:
类型安全:
ArrayList存储object类型,无编译期类型校验,取值需强制类型转换;List<T>是泛型集合,指定具体类型,编译期校验类型,避免类型错误。性能:
ArrayList的元素读写会触发 “装箱 / 拆箱”(值类型与object的转换),产生性能开销;List<T>避免装箱拆箱,性能更高。接口规范:
List<T>实现IList<T>等泛型接口,符合强类型规范;ArrayList仅继承非泛型IList,类型适配性差。
推荐 List<T>的原因:① 类型安全,编译期排查类型错误,减少运行时异常;② 无装箱拆箱开销,性能更优;③ 代码可读性、可维护性更高,无需频繁强制类型转换;④ 更好地兼容现代 C# 的泛型生态。
11.C# 中如何遍历 Dictionary 集合?
C# 遍历Dictionary<TKey, TValue>的核心方式:
遍历键值对:直接遍历
Dictionary实例,获取每个KeyValuePair<TKey, TValue>(包含键和值);遍历键集合:通过
Keys属性获取所有键的集合,遍历键后通过 “字典 [键]” 取值;遍历值集合:通过
Values属性直接遍历所有值(无法获取对应键);枚举器遍历:通过
GetEnumerator()获取泛型枚举器,手动控制遍历过程(底层实现逻辑);注意:遍历过程中若增删
Dictionary元素会抛出异常,需先复制键集合(如ToList())再遍历。
12.值类型和引用类型的区别?
存储位置:值类型存储在栈(或嵌入到引用类型的堆内存中);引用类型数据存储在堆,栈中仅保存指向堆内存的引用地址。
赋值规则:值类型赋值是拷贝值本身,赋值后两个变量相互独立;引用类型赋值是拷贝引用地址,赋值后多个变量指向同一块堆内存,修改一个会影响另一个。
继承与 null:值类型继承自
ValueType,默认不可为 null(可空值类型除外);引用类型继承自Object,默认值为 null(无指向的堆内存)。内存回收:值类型超出作用域后自动释放;引用类型由 GC(垃圾回收器)回收,需等待 GC 触发,存在析构 / 终结器机制。
特性:值类型无继承(除隐式继承
ValueType),引用类型支持继承和多态;值类型有默认值(如 int 默认 0),引用类型默认无实际对象。
13.什么是装箱和拆箱?
装箱:将值类型隐式转换为
object类型(或其基类)的过程;本质是在堆上分配内存,将值类型的数值拷贝到堆内存中,栈中保存指向该堆内存的引用,会产生性能开销。拆箱:将装箱后的
object类型显式转换回原值类型的过程;本质是先校验堆中类型与目标值类型是否匹配,再将堆中的值拷贝回栈,拆箱失败会抛出类型转换异常,同样有性能开销。
14.using 关键字的作用
作用 1:引入命名空间:简化类型引用,无需书写完整的命名空间路径即可使用该命名空间下的类型。
作用 2:资源释放(using 块):用于管理实现
IDisposable接口的资源(如文件流、数据库连接),using 块结束时会自动调用资源的Dispose方法,即使块内抛出异常也会执行,避免非托管资源泄漏;C# 8.0 + 支持 using 声明,变量声明时加 using,作用域结束自动释放资源。
15.什么是委托(Delegate)?与事件(Event)有什么关系?
委托(Delegate):是 C# 中类型安全的函数指针,可封装一个或多个匹配签名(返回值 + 参数)的方法引用,支持多播(一个委托实例绑定多个方法),本质是继承自
MulticastDelegate的特殊类,核心用于实现方法的间接调用、回调逻辑。与事件(Event)的关系:事件是基于委托的封装(“委托的包装器”),本质是特殊的多播委托;事件限制了委托的访问权限(外部只能通过
+=订阅、-=取消订阅,无法直接调用或赋值委托实例),遵循发布 - 订阅模式,增强了封装性和安全性,避免委托被外部随意篡改或调用。