*委托:
c#中的委托类似于c或者c++中函数的指针。
委托是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变(可以理解为是一种数据类型)
委托用于实现事件和回调方法
// 声明一个委托类型MyDelegate
// 委托是一种引用类型,用于封装具有特定参数列表和返回类型的方法
// 格式:访问修饰符 delegate 返回值类型 委托名称(参数列表)
// 这里声明的委托可以指向任何返回bool类型且带有一个string参数的方法
public delegate bool MyDelegate(string v);
// 命名空间:用于组织代码,避免命名冲突
namespace 委托
{
// 程序主类
internal class Program
{
// 程序入口点
static void Main(string[] args)
{
// 演示委托的使用:将方法作为参数传递给另一个方法
// 这里将MyFn方法作为参数传递给Test.TestFn
Test.TestFn(MyFn);
// 这里将MyFn1方法作为参数传递给Test.TestFn
Test.TestFn(MyFn1);
}
// 定义一个符合MyDelegate委托签名的方法
// 签名匹配:返回值为bool,参数为一个string
static bool MyFn(string a)
{ Console.WriteLine("印度制造" + a);
return true; }
// 定义另一个符合MyDelegate委托签名的方法
static bool MyFn1(string b)
{ Console.WriteLine("中国制造" + b);
return true; }
}
// 测试类,用于演示委托的接收和使用
class Test
{
// 定义一个静态方法,参数为MyDelegate类型
// 这意味着该方法可以接受任何符合MyDelegate签名的方法作为参数
public static void TestFn(MyDelegate f)
{
// 通过委托变量调用所引用的方法
// 这里传递固定参数"苹果手机"给被委托的方法
f("苹果手机");
}
}
}
•委托的实例化:
// 创建第一个委托类型MyFns
// 该委托可以指向:无返回值(void),参数为int和string的方法
delegate void MyFns(int v1, string v2);
// 创建第二个委托类型Number
// 该委托可以指向:返回值为int,参数为两个int的方法
delegate int Number(int v1, int v2);
// 命名空间:组织相关代码
namespace 委托的实例化
{
// 程序主类
internal class Program
{
// 程序入口方法
static void Main(string[] args)
{
// 创建Test类的实例,会自动调用其构造函数
new Test();
}
}
// 测试类,用于演示委托的实例化和使用
class Test
{
// Test类的构造函数
public Test()
{
// 1. 完整语法实例化MyFns委托
// 将Fn1方法作为参数传递给委托构造函数
MyFns fns1 = new MyFns(Fn1);
// 直接调用Fn1方法(普通方法调用)
Fn1(1, "吴亦凡");
// 通过委托变量调用方法(委托调用方式1)
// 委托变量可以像方法一样直接调用
fns1(1, "蔡徐坤");
// 2. 实例化Number委托并使用
// 将Sum方法关联到Number类型的委托变量
Number number = new Number(Sum);
// 直接调用Sum方法
Console.WriteLine(Sum(10, 20));
// 通过委托调用Sum方法
Console.WriteLine(number(20, 30));
// 3. 委托实例化的简写形式
// 不需要显式使用new关键字,直接赋值方法名
Number number1 = Sum;
// 通过简写形式创建的委托调用方法
Console.WriteLine(number1(5, 6));
// 4. 使用Invoke方法调用委托(委托调用方式2)
// 这是委托调用的标准写法,与直接调用等效
Console.WriteLine(number1.Invoke(1, 3));
}
// 符合MyFns委托签名的方法
// 无返回值,参数为int和string
void Fn1(int a, string b)
{ Console.WriteLine($"a={a},b={b}"); }
// 符合Number委托签名的方法(乘法)
int Cheng(int a, int b)
{ return a * b; }
// 符合Number委托签名的方法(加法)
int Sum(int a, int b)
{ return a + b; }
}
}
•多播委托:
// 声明一个委托类型MyDelegate
// 该委托可指向:无返回值,参数为string的方法
delegate void MyDelegate(string name);
// 命名空间:多播委托演示
namespace _多播委托
{
internal class Program
{
static void Main(string[] args)
{
// 多播委托:可以包含多个方法的委托实例
// 当调用多播委托时,会按添加顺序依次执行所有包含的方法
// 1. 基础多播委托示例
// 创建委托实例并关联第一个方法Fn1
MyDelegate fns = new MyDelegate(Fn1);
// 使用+=运算符添加第二个方法(Test类的实例方法Fn2)
fns += new MyDelegate(new Test().Fn2);
// 调用多播委托:会依次执行Fn1和Fn2,并将参数"吴亦凡"传递给两个方法
fns("吴亦凡");
// 2. 多播委托简写形式
// 简写方式创建委托并关联第一个方法
MyDelegate fns1 = Fn1;
// 简写方式添加第二个方法
fns1 += new Test().Fn2;
// 调用多播委托
fns1("蔡徐坤");
Console.WriteLine("-----------------");
// 3. 多播委托移除方法的注意事项(错误示例)
MyDelegate fns2 = Fn1;
fns2 += new Test().Fn2; // 添加一个匿名Test实例的Fn2方法
fns2 -= new Test().Fn2; // 尝试移除,但这里创建了新的Test实例,与添加时的实例不同
// 实际执行时Fn2仍会被调用,因为移除的不是同一个实例的方法
fns2.Invoke("范冰冰");
// 4. 解释上述错误的原因
MyDelegate fns4 = Fn1;
Test t1 = new Test(); // 创建第一个Test实例
Test t2 = new Test(); // 创建第二个Test实例(与t1不是同一个对象)
fns4 += t1.Fn2; // 添加t1实例的Fn2
fns4 -= t2.Fn2; // 尝试移除t2实例的Fn2(与添加的不是同一个方法引用)
// 结果:t1.Fn2仍会被执行,因为移除的不是同一个方法
// 5. 正确移除实例方法的解决方案
Console.WriteLine("---------------------");
MyDelegate fns3 = Fn1;
Test t = new Test(); // 创建一个Test实例并保存引用
fns3 += t.Fn2; // 添加该实例的Fn2
fns3 -= t.Fn2; // 移除同一个实例的Fn2(正确方式)
fns3.Invoke("abc"); // 此时只会执行Fn1
// 6. 移除静态方法的示例(静态方法无需实例,直接通过类名访问)
Console.WriteLine("---------------------");
MyDelegate fns5 = Fn1;
fns5 += Test.Fn3; // 添加静态方法Fn3
fns5 -= Test.Fn3; // 移除静态方法Fn3(静态方法移除无需考虑实例)
fns5("cba"); // 此时只会执行Fn1
}
// 符合MyDelegate委托签名的静态方法
public static void Fn1(string v)
{ Console.WriteLine($"这是Fn1中的v:{v}"); }
}
// 测试类,包含实例方法和静态方法
class Test
{
// 实例方法:必须通过类的实例调用
public void Fn2(string v)
{ Console.WriteLine($"这是Fn2中的v:{v}"); }
// 静态方法:可直接通过类名调用,无需实例
public static void Fn3(string v)
{ Console.WriteLine($"这是Fn3中的v:{v}"); }
}
}
•委托的调用:
// 命名空间:演示委托的各种调用方式
namespace 委托的调用
{
internal class Program
{
static void Main(string[] args)
{
// 创建Test类的实例
Test t = new Test();
// 调用Run方法,该方法中包含委托调用的演示逻辑
t.Run();
}
}
class Test
{
// 声明一个委托类型MyDelegate
// 该委托可指向:返回值为int,参数为int的方法
public delegate int MyDelegate(int x);
// 声明一个MyDelegate类型的属性,用于存储委托方法
public MyDelegate Fns { get; set; }
// 构造函数:初始化委托并添加方法
public Test()
{
// 向委托属性中添加方法(多播委托)
Fns += Fn1; // 关联Fn1方法
Fns += Fn2; // 关联Fn2方法
}
// 符合MyDelegate委托签名的方法1
public int Fn1(int v)
{
Console.WriteLine(v * 10); // 输出输入值的10倍
return v * 10; // 返回计算结果
}
// 符合MyDelegate委托签名的方法2
public int Fn2(int v)
{
Console.WriteLine(v * v); // 输出输入值的平方
return v * v; // 返回计算结果
}
// 声明一个People类型的字段(未初始化,默认值为null)
People p;
public void Run()
{
// 1. 直接调用委托(多播委托会按添加顺序执行所有方法)
// Fns(20); // 此行与下面的Invoke调用等效
// 2. 安全调用委托的方式1:判断委托是否为null
/*if (Fns != null)
{
Fns(20); // 当委托不为null时才调用,避免NullReferenceException
}*/
// 3. 安全调用委托的方式2:使用空值条件运算符?.
// 当Fns为null时,不执行Invoke,也不会报错
Fns?.Invoke(20);
// 空值条件运算符?.的其他用法:访问可能为null的对象的成员
// 当p为null时,p?.Name会返回null,不会抛出异常
Console.WriteLine(p?.Name == null); // 此时p为null,输出True
// 初始化p并赋值
p = new People();
p.Name = "蔡徐坤";
// 当p不为null时,正常访问Name属性
Console.WriteLine(p?.Name); // 输出"蔡徐坤"
}
}
// 辅助类:用于演示空值运算符的使用
class People
{ public string Name { get; set; } // Name属性
}
}
•内置委托:
// 命名空间:演示委托的各种调用方式
namespace 委托的调用
{
internal class Program
{
static void Main(string[] args)
{
// 创建Test类的实例
Test t = new Test();
// 调用Run方法,该方法中包含委托调用的演示逻辑
t.Run();
}
}
class Test
{
// 声明一个委托类型MyDelegate
// 该委托可指向:返回值为int,参数为int的方法
public delegate int MyDelegate(int x);
// 声明一个MyDelegate类型的属性,用于存储委托方法
public MyDelegate Fns { get; set; }
// 构造函数:初始化委托并添加方法
public Test()
{
// 向委托属性中添加方法(多播委托)
Fns += Fn1; // 关联Fn1方法
Fns += Fn2; // 关联Fn2方法
}
// 符合MyDelegate委托签名的方法1
public int Fn1(int v)
{
Console.WriteLine(v * 10); // 输出输入值的10倍
return v * 10; // 返回计算结果
}
// 符合MyDelegate委托签名的方法2
public int Fn2(int v)
{
Console.WriteLine(v * v); // 输出输入值的平方
return v * v; // 返回计算结果
}
// 声明一个People类型的字段(未初始化,默认值为null)
People p;
public void Run()
{
// 1. 直接调用委托(多播委托会按添加顺序执行所有方法)
// Fns(20); // 此行与下面的Invoke调用等效
// 2. 安全调用委托的方式1:判断委托是否为null
/*if (Fns != null)
{
Fns(20); // 当委托不为null时才调用,避免NullReferenceException
}*/
// 3. 安全调用委托的方式2:使用空值条件运算符?.
// 当Fns为null时,不执行Invoke,也不会报错
Fns?.Invoke(20);
// 空值条件运算符?.的其他用法:访问可能为null的对象的成员
// 当p为null时,p?.Name会返回null,不会抛出异常
Console.WriteLine(p?.Name == null); // 此时p为null,输出True
// 初始化p并赋值
p = new People();
p.Name = "蔡徐坤";
// 当p不为null时,正常访问Name属性
Console.WriteLine(p?.Name); // 输出"蔡徐坤"
}
}
// 辅助类:用于演示空值运算符的使用
class People
{ public string Name { get; set; } // Name属性
}
}
•泛滥委托:
// 声明泛型委托MyDelegate<T>
// 泛型委托可以适用于多种数据类型,增强了代码的复用性
// 该委托接受一个T类型的参数,返回bool类型
delegate bool MyDelegate<T>(T v);
// 命名空间:泛型委托演示
namespace 泛型委托
{
internal class Program
{
static void Main(string[] args)
{
// 字符串数组示例
string[] strings = { "徐坤1", "蔡徐坤", "吴亦凡" };
// 注释:使用泛型方法筛选以"蔡"开头的字符串
/*string s = FnList<string>(strings, Fn1);
Console.WriteLine(s);*/
// 整数数组示例:使用泛型委托筛选数组中的偶数
int[] ints = { 1, 2, 3, 4, 5, 6 };
// 调用泛型方法FnList,传入int数组和筛选方法Fn2
List<int> resultList = FnList(ints, Fn2);
// 输出筛选结果
foreach (var item in resultList)
{ Console.WriteLine(item); }
}
// 字符串筛选方法:判断字符串是否以"蔡"开头
// 符合MyDelegate<string>或Func<string, bool>的签名
static bool Fn1(string name)
{ return name.StartsWith("蔡"); }
// 整数筛选方法:判断整数是否为偶数
// 符合MyDelegate<int>或Func<int, bool>的签名
static bool Fn2(int value)
{ return value % 2 == 0; }
// 泛型方法:使用泛型委托筛选数组元素
// T:泛型类型参数,可代表任意数据类型
// 参数1:T类型的数组,需要筛选的数据源
// 参数2:Func<T, bool>委托,用于定义筛选条件(也可使用自定义的MyDelegate<T>)
public static List<T> FnList<T>(T[] Arrs, Func<T, bool> filterFunc)
{
// 创建一个列表用于存储筛选结果
List<T> resultList = new List<T>();
// 遍历数组中的每个元素
for (int i = 0; i < Arrs.Length; i++)
{
// 调用委托方法判断当前元素是否符合筛选条件
// 如果符合条件(返回true),则添加到结果列表
if (filterFunc(Arrs[i]))
{ resultList.Add(Arrs[i]); }
}
// 返回筛选后的结果列表
return resultList;
}
}
}
2万+

被折叠的 条评论
为什么被折叠?



