C#进阶
Arraylist
ArryList array = new ArrayList();
Stack
本质是object[]数组,后进先出
Stack stack = new Stack();
//压栈
stack.push(1);
//取
object v = stack.Pop();
//查,只能查看栈顶的内容
v = stack.peek;
//查看是否存在于栈中
stack.contains("123");
//清空
stack.Clear();
//遍历
stack.Count;
foreach(object item in stack)
{
Console.WriteLine(item);
}
//将栈转换为object数组,遍历出来的顺序也是从栈顶到栈底
object[] array = stack.ToArray();
//循环弹栈
while(stack.Count > 0)
{
object o = stack.Pop();
Console.WriteLine(stack.Count);
}
Queue
//队列本质也是object[]数组,先进先出
Queue q = new Queue();
//增
Queue.Enqueue(1);
//取
object v = queue.Dequeue();
//查看队列头部元素但不会移除
v = queue.Peek();
//清空
queue.Clear();
//遍历
foreach(object item in queue)
{
Console.WriteLine(item);
}
//循环出列
while(queue.Count>0)
{
object o = queue.Dequeue();
}
Hashtable
//基于键的哈希代码组织起来的键/值对,主要作用是提高数据查询的效率
Hashtable hashtable = new Hashtable();
//增
hashtable.Add(1,"123");
hashtable.Add("123",2);
//注意:不能出现相同的键
//删,只能通过键去删除
hashtable.Remove(1);
//直接清空
hashtable.Clear();
//查,通过键查看值
Console.WriteLine(hashtable[1]);
//查看是否存在
//根据键检测
if(hashtable.ContainsKey(2))
{
return true;
}
//根据值检测
if(hashtable.ContainsValue(1))
{
return true;
}
//改,只能改键对应的值,无法修改键
hashtable[1]=100.2f;
//遍历
//遍历所有键
foreach(object item in hashtable.Keys)
{
Console.WriteLine("键:"+item);
Console.WriteLine("值:"+hashtable[item]);
}
//遍历所有值
foreach(object item in hashtable.Values)
{
Console.WriteLine("值:"+item);
}
//键值对一起遍历
foreach(DictionaryEntry in hashtable)
{
Console.WriteLine("键:"+item.key+"值:"+item.velue);
}
泛型
//泛型类
class TestClass<T>
{
public T value;
}
//泛型接口
interface TestInterface<T>
{
T value{get;set;}
}
class Test:TestInterface<int>
{
public int value{get=>throw new Exception();set=>throw new Exception();}
}
class Program
{
static void Main(string[] args)
{
TestClass<int> t = new TestClass<int>();
t.value = 1;
TestClass<string> t2 = new TestClass<string>();
t2.value = "123";
}
}
//普通类中的泛型方法
class Test2
{
public T TestFun<T>(T value)
{
return value;
}
public void TestFun<T>()
{
//用泛型类型,在里面做一些逻辑处理
T t = default(T);
}
}
//泛型类中的泛型方法
class Test2<t>
{
public T value;
public void TestFun<K>(K k)
{
console.WriteLine(k);
}
}
泛型约束
//值类型
class Test1<T> where T:struct
{
}
//引用类型
class Test1<T> where T:class
{
}
//存在公共无参构造函数
class Test1<T> where T:new()
{
}
List
本质是一个可变类型的泛型数组
//声明
List<int> list = new List<int>();
List<string> list2 = new List<string>();
//增
list.Add(1);
list2.Add("123");
//删
list.Remove(1);
//查
Console.WriteLine(list[0]);
//改
list[0] = 99;
Dictionary
拥有泛型的Hashtable
Dictioary<int,string> dic = new Dictionary<int,string>();
//增,不能出现相同键
dic.Add(1,"123");
dic.Add(2,"111");
//删,只能通过键去删除
dic.Remove(1);
//清空
dic.Clear();
//查,通过键查看值,找不到直接报错
Console.WriteLine(dic[1]);
//改
dic[1] = "222";
顺序存储和链式存储
顺序存储:用一组地址连续的存储单元依次存储线性表的各个数据元素
链式存储:用一组任意的存储单元存储线性表中的各个数据元素
委托
1.自定义委托:
通过 delegate 关键字声明,格式为 delegate 返回值类型 委托名(参数列表):
点击查看代码
// 定义无返回值、接收int参数的委托
delegate void MyDel(int i);
class Program
{
static void Main(string[] args)
{
// 实例化委托并绑定方法
MyDel d1 = F1;
MyDel d2 = F2;
// 调用委托(本质调用绑定方法)
d1(5); // 输出:我是F1:5
d2(5); // 输出:我是F2:5
}
// 符合MyDel签名的目标方法
static void F1(int i) => Console.WriteLine("我是F1:"+i);
static void F2(int i) => Console.WriteLine("我是F2:" + i);
}
2.多播委托:
委托支持通过 +=(或 +)组合多个方法,调用时按绑定顺序依次执行,也可通过 -= 移除方法。需注意多线程下的线程安全:
点击查看代码
// 基础多播委托用法
MyDel d1 = F1;
MyDel d2 = F2;
MyDel d3 = F3;
MyDel d4 = d1 + d2 + d3;
d4(8); // 依次执行F1、F2、F3
// 多线程下的安全组合(锁保护字段原子性)
private MyDel _delegate;
private readonly object lockObj = new object();
public void AddDelegate(MyDel newDelegate)
{
lock (lockObj)
{
_delegate = (MyDel)Delegate.Combine(_delegate, newDelegate);
}
}
内置泛型委托:Func/Action(优先使用)
实际开发中无需重复定义委托,.NET 内置的 Func 和 Action 覆盖 90% 以上场景:

(1)Action 示例
点击查看代码
// 无参数Action
Action printHello = () => Console.WriteLine("Hello Action");
printHello();
// 接收2个参数的Action
Action<string, int> log = (msg, level) => Console.WriteLine($"[{level}] {msg}");
log("数据更新成功", 1); // 输出:[1] 数据更新成功
(2)Func 示例
点击查看代码
// 无参数,返回string(当前时间)
Func<string> getTime = () => DateTime.Now.ToString("HH:mm:ss");
Console.WriteLine(getTime());
// 接收2个int参数,返回int(求和)
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 5)); // 输出:8
// LINQ中应用(筛选偶数)
List<int> numbers = new List<int> {1,2,3,4,5};
var evenNumbers = numbers.Where(n => n % 2 == 0); // Where接收Func<int, bool>参数
进阶特性:异步委托与闭包陷阱
(1)异步委托
支持绑定异步方法,需声明返回 Task 的委托类型,调用时通过 await 处理:
点击查看代码
// 定义异步委托
public delegate Task AsyncDelegate(int x);
// 绑定异步方法
AsyncDelegate del = async x => {
await Task.Delay(100).ConfigureAwait(false); // 避免上下文捕获
return x * 2;
};
// 异步调用
try {
int result = await del(5);
} catch (Exception ex) {
// 捕获异步异常
}
(2)Lambda 闭包陷阱
循环中 Lambda 捕获的是变量引用而非值,易导致结果不符合预期:
点击查看代码
// 问题代码:所有按钮点击后均输出3
for (int i = 0; i < 3; i++) {
buttons[i].OnClick += () => Console.WriteLine(i);
}
// 修复方案:创建局部副本
for (int i = 0; i < 3; i++) {
int current = i;
buttons[i].OnClick += () => Console.WriteLine(current);
}
事件
事件是委托的安全包装器,基于委托实现但通过访问控制限制外部操作,核心区别如下:

事件的定义与使用
通过 event 关键字声明,通常结合 EventHandler
点击查看代码
// 自定义事件参数(继承EventArgs)
public class AgeChangedEventArgs : EventArgs {
public int NewAge { get; }
public AgeChangedEventArgs(int newAge) => NewAge = newAge;
}
public class Person
{
private int _age;
// 声明强类型事件(推荐)
public event EventHandler<AgeChangedEventArgs> OnBenMingNian;
public int Age
{
get => _age;
set
{
if (value == _age) return;
_age = value;
// 触发事件(空条件运算符避免空引用)
if (value % 12 == 0)
{
OnBenMingNian?.Invoke(this, new AgeChangedEventArgs(value));
}
}
}
}
// 调用示例
class Program
{
static void Main(string[] args)
{
Person p = new Person();
// 订阅事件
p.OnBenMingNian += (sender, e) => Console.WriteLine($"本命年到了,年龄:{e.NewAge}");
p.Age = 24; // 触发事件,输出:本命年到了,年龄:24
}
}
事件的关键问题:内存泄漏
(1)典型泄漏场景
静态事件未取消订阅;
订阅者未在 Dispose 中取消订阅;
长生命周期发布者持有短生命周期订阅者引用。
(2)解决方案
实现 IDisposable 模式,主动取消订阅:
点击查看代码
public class AlarmSystem : IDisposable
{
private readonly Sensor _sensor;
public AlarmSystem(Sensor sensor)
{
_sensor = sensor;
_sensor.TemperatureChanged += OnTemperatureChange;
}
private void OnTemperatureChange(object sender, TemperatureChangedEventArgs e)
{
if (e.NewValue > 50) Console.WriteLine("高温警报!");
}
// 取消订阅,避免内存泄漏
public void Dispose()
{
_sensor.TemperatureChanged -= OnTemperatureChange;
}
}
WPF 场景使用 WeakEventManager;
采用事件私有封装模式,避免外部直接操作。
委托与事件的性能优化与调试
委托链性能优化
委托链长度过大会影响性能,建议生产环境控制在 5 以内,可通过 GetInvocationList() 监控:
点击查看代码
Delegate[] invocables = _delegate.GetInvocationList();
Debug.WriteLine($"委托链长度:{invocables.Length}");
if (invocables.Length > 5)
{
// 优化:拆分委托链或调整订阅逻辑
}
事件触发优化
优先使用空条件运算符 ?.Invoke(),替代传统的空值判断,编译器会自动转换为更高效的 DelegateExtensions.Invoke 方法:
点击查看代码
// 传统方式(不推荐)
if (OnEvent != null) OnEvent(sender, e);
// 推荐方式(C#6+)
OnEvent?.Invoke(sender, e);
综合实战:观察者模式(委托 + 事件核心应用)
观察者模式是委托与事件的典型落地场景,实现发布者 - 订阅者的松耦合通信:
点击查看代码
// 自定义事件参数
public class TemperatureChangedEventArgs : EventArgs
{
public float NewValue { get; }
public TemperatureChangedEventArgs(float newValue) => NewValue = newValue;
}
// 发布者:传感器(温度变更通知)
public class Sensor : IDisposable
{
private float _temperature;
// 声明温度变更事件
public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
public float Temperature
{
get => _temperature;
set
{
if (value != _temperature)
{
_temperature = value;
// 触发事件
TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs(value));
}
}
}
public void Dispose()
{
// 清空事件订阅,避免内存泄漏
TemperatureChanged = null;
}
}
// 订阅者:警报系统
public class AlarmSystem : IDisposable
{
private readonly Sensor _sensor;
public AlarmSystem(Sensor sensor)
{
_sensor = sensor;
_sensor.TemperatureChanged += OnTemperatureChange;
}
private void OnTemperatureChange(object sender, TemperatureChangedEventArgs e)
{
if (e.NewValue > 50) Console.WriteLine("高温警报!");
}
// 取消订阅,释放资源
public void Dispose()
{
_sensor.TemperatureChanged -= OnTemperatureChange;
}
}
// 调用示例
class Program
{
static void Main(string[] args)
{
using (var sensor = new Sensor())
using (var alarm = new AlarmSystem(sensor))
{
sensor.Temperature = 40; // 无警报
sensor.Temperature = 55; // 输出:高温警报!
} // 自动调用Dispose,取消订阅
}
}
特性(Attribute)
特性本质上是一个继承自 System.Attribute 的特殊类。
它的作用不是“执行代码”,而是:
在编译期写入程序集元数据,在运行期通过反射读取的声明式信息。
关键特征:
✔ 编译期生成,存储在程序集元数据中
✔ 运行期只读,无法被修改
❌ 不会自动生效、不包含业务逻辑
✔ 必须配合反射或框架解析才有意义
Attribute = 描述规则
Reflection / Framework = 执行规则


自定义特性
.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。
创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一章详细讨论这点。
声明自定义特性
一个新的自定义特性应派生自 System.Attribute 类。例如:
点击查看代码
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
构建自定义特性
让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:
bug 的代码编号
辨认该 bug 的开发人员名字
最后一次审查该代码的日期
一个存储了开发人员标记的字符串消息
我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。
每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:
点击查看代码
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
应用自定义特性
通过把特性放置在紧接着它的目标之前,来应用该特性:
点击查看代码
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
反射(Reflection)
反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
优点:
1、反射提高了程序的灵活性和扩展性。
2、降低耦合性,提高自适应能力。
3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:
1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。
反射(Reflection) 是 .NET 框架提供的核心运行时机制,它允许程序在运行时而非编译时:
获取程序集(Assembly)、模块(Module)、类型(Type)的元数据信息
动态创建对象实例
动态调用方法
动态读取或修改属性/字段
动态解析自定义特性(Attribute)




浙公网安备 33010602011771号