C# 委托 - 快速了解使用

本文深入探讨C#中的委托,包括定义、使用、Action和Func委托的差异、委托案例如雇员冒泡排序、多播委托的概念以及匿名方法和Lambda表达式。此外,还详细介绍了C#事件的机制,通过猫和老鼠的观察者模式展示了事件在实际编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

委托

1. 什么是委托

  • 如果我们要把方法当做参数来传递的话,就要用到委托。简单来说委托是一个类型,这个类型可以赋值一个方法的引用

2. 委托的定义和使用

  • 委托的使用跟Class类似,首先需要定义委托,指定委托对应方法的参数和返回值,以告诉编译器我们这个委托可以指向哪些类型的方法,然后再创建该委托的实例
private delegate string GetAString();//定义了一个委托类型,这个委托类型的名字叫做GetAString
方法内部
//示例1 委托的赋值和使用的两种方式
int x = 40;
//使用委托类型 创建实例
GetAString a = new GetAString(x.ToString);//a指向了x中的tostring方法
GetAString a = x.ToString; //常用赋值方式
string s = a();//通过委托实例去调用 x中的tostring方法
string s = a.Invoke();//通过invoke方法调用a所引用的方法
Console.WriteLine(s);//通过委托类型去调用一个方法,跟直接调用这个方法作用是一样的

3. Action 委托

  • Action 委托引用了一个void返回类型的方法,T表示方法参数
Action
Action<in T>
Action<in T1,in T2>
Action<in T1,in T2 ....  in T16>
//示例开始    
Action a = PrintString;//action是系统内置(预定义)的一个委托类型,它可以指向一个没有返回值,没有参数的方法
Action<int> a = PrintInt;//定义了一个委托类型,这个类型可以指向一个没有返回值,有一个int参数的方法
Action<string> a = PrintString;//定义了一个委托类型,这个类型可以指向一个没有返回值,有一个string参数的方法,如果有重名方法,系统会自动寻找匹配的方法
Action<int, int> a = PrintDoubleInt;
a(34, 23);
//action可以后面通过泛型去指定action指向的方法的多个参数的类型 ,参数的类型跟action后面声明的委托类型是对应着的
//PrintString(+1 重载), PrintInt, PrintDoubleInt 静态方法的实现略

4. Func 委托

  • Func引用了一个带有一个返回值的方法,它可以传递0或者多到16个参数类型,和一个返回类型
Func<out TResult>
Func<in T,out TResult>
Func<in T1,in T2 .... in T16,out TResult>
//示例开始
//func后面必须指定一个返回值类型,参数类型可以有0-16个,先写参数类型,最后一个是返回值类型
Func<int, int, int> a = Test3;
int res = a(1, 5);
//方法的定义
static int Test3(int i, int j)
{
	return i + j;
}

5. 委托案例 之 雇员按工资冒泡排序

//排序方法的参数为泛型数组和用于比较两个泛型大小的委托
static void CommonSort<T>(T[] sortArray, Func<T, T, bool> compareMethod)
{
    bool swapped = true;
    do
    {
        swapped = false;
        for (int i = 0; i < sortArray.Length - 1; i++)
        {
            if (compareMethod(sortArray[i], sortArray[i + 1]))
            {
                T temp = sortArray[i];
                sortArray[i] = sortArray[i + 1];
                sortArray[i + 1] = temp;
                swapped = true;
            }
        }
    } while (swapped);
}
//雇员类,按工资排序
class Employee
{
    //字段定义,构造方法略,只看比较方法
    public static bool Compare(Employee e1, Employee e2)
    {
        if (e1.Salary > e2.Salary) return true;
        return false;
    }  
}
//委托的调用(方法内)
Employee[] employees = new Employee[]
{
    new Employee("dsf",12),
    new Employee("435dsf",234),
    new Employee("234dsf",14),
    new Employee("ds234f",234),
};
CommonSort<Employee>(employees, Employee.Compare);
foreach (Employee em in employees)
{
    Console.WriteLine(em);
}

6. 多播委托

  • 前面使用的委托都只包含一个方法的调用,但是委托也可以包含多个方法,这种委托叫做多播委托
  • 使用多播委托就可以按照顺序调用多个方法,多播委托只能得到调用的最后一个方法的结果,一般我们把多播委托的返回类型声明为void
  • 多播委托包含一个逐个调用的委托集合,如果通过委托调用的其中一个方法抛出异常,整个迭代就会停止
//多播委托
Action a = Test1;
a += Test2;//表示添加一个委托的引用 
a -= Test1;//删除一个委托的引用
a -= Test2;
if(a!=null)
{
   a();//当一个委托没有指向任何方法的时候,调用的话会出现异常null 
}
//方法的定义
static void Test1()
{
    Console.WriteLine("test1");
    //throw new Exception();
}

static void Test2()
{
	Console.WriteLine("test2");
}	
//取得多播委托中所有方法的委托  
Delegate[] delegates = a.GetInvocationList();           
foreach (Delegate de in delegates)            
{             
    de.DynamicInvoke();            
}

7. 匿名方法

  • 使用委托的正常步骤是先定义一个方法,然后把方法给委托的实例。但还有另外一种使用委托的方式,不用去定义一个方法,应该说是使用匿名方法(方法没有名字)。相当于直接把要引用的方法直接写在了后面,优点是减少了要编写的代码,减少代码的复杂性
Func<int,int,int> plus = delegate (int a,int b)
{
   	int temp = a+b;
   	return temp;
};
int res = plus(34,34);
Console.WriteLine(res);

8. Lambda表达式

  • Lambda表达式用于简化匿名方法的定义,
  • Lambda运算符 “=>” 的左边列出了需要的参数,如果是一个参数可以直接写 a=>(参数名自己定义),如果多个参数就使用括号括起来,参数之间以逗号间隔
  • 参数只有一个的时候,可以不加上括号
  • 当函数体的语句只有一句的时候,我们可以不加上大括号,也可以不加上return语句
  • 通过Lambda表达式可以访问Lambda表达式块外部的变量,危险不推荐使用
Func<int, int, int> plus = (arg1, arg2) =>// lambda表达式的参数是不需要声明类型的
{
	return arg1 + arg2;
};
//lambda表示的参数只有一个的时候,可以不加上括号  
//当函数体的语句只有一句的时候,我们可以不加上大括号,也可以不加上return语句
Func<int, int> test2 = a => a+1;
Func<int, int> test3 = (a) =>
{
	return a + 1;
};
Console.WriteLine(test2(34));
Console.WriteLine(test3(34));

9. 事件

  • 事件(event)基于委托,为委托提供了一个发布/订阅机制,我们可以说事件是一种具有特殊签名的委托
  • 事件使用event关键词来声明,它的返回类值是一个委托类型,习惯以名字+ Event 作为事件的名称
  • event只允许用add, remove方法来操作,这导致了它不允许在类的外部被直接触发,只能在类的内部适合的时机触发。委托可以在外部被触发,但是别这么用
  • 使用中,委托常用来表达回调,事件表达外发的接口
  • 事件的定义
delegate void ActionHandler(object sender, EventArgs args); //先定义委托类型
event ActionHandler Tick; //再定义事件实例
ActionHandler act = (s,arg) => { s.ToString(); }; //委托的实例化
Tick += act;

10. event 示例之 猫和老鼠 (观察者设计模式)

  • Cat 作为被观察者,一旦猫的状态有改变,观察者 Mouse需要立刻做出反应,这可以通过委托来实现,为了防止委托在Cat类外被调用,使用事件来实现
class Cat
{
    private string name;
    private string color;
    //event + 委托类型 + 事件名
    public event Action catCome;//声明一个事件 发布了一个消息 
    // 猫进屋(猫的状态发生改变)(被观察者的状态发生改变)
    public void CatComing()
    {
        Console.WriteLine(color + "的猫" + name + "过来了,喵");
        if (catCome != null)
            catCome(); //通过事件传递消息给观察者
    }
}

class Mouse
{
    private string name;
    private string color;

    public Mouse(string name, string color, Cat cat)
    {
        this.name = name;
        this.color = color;
        cat.catCome += this.RunAway;//把自身的逃跑方法,注册进猫里面(订阅消息)
    }
    ///做出反应
    public void RunAway()
    {
        Console.WriteLine(color + "的老鼠" + name + "说: 老猫来,我跑");
    }
}
//调用
class Program {
        static void Main(string[] args) {
            Cat cat = new Cat("加菲猫","黄色");
            Mouse mouse1 = new Mouse("米奇","黑色",cat);
            Mouse mouse2 = new Mouse("唐老鸭", "红色",cat);
            cat.CatComing(); //被观察者状态改变,传递消息
            //注册的方法触发
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值