C#委托和事件

本文详细介绍了C#中的委托、事件及其应用,包括委托的基本语法、自定义委托、事件的定义与使用,以及匿名函数和Lambda表达式的概念、语法和使用场景。通过这些内容,读者能够更好地理解和掌握C#中函数的封装和传递机制。

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

@作者: 风不停息丶 笔记
在这里插入图片描述


C#委托

1、什么是委托

  • 委托是函数(方法)的容器,可以理解为表示函数(方法)的变量类型
  • 用来存储、传递函数(方法)
  • 委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
  • 不同的函数(方法)必须对应和各自"格式"一致的委托

2、基本语法

  • 委托语法就是函数申明语法前面加一个delegate关键字
  • 语法:访问修饰符 delegate 返回值 委托名(参数列表)
  • 可以申明在namespace和class语句块中
public delegate int MyFun(int a);

3、自定义委托

  • 访问修饰默认不写(public)在别的命名空间中也能使用
  • private其它命名空间就不能用,一般使用public
//申明了一个可以用来存储无参无返回值函数的容器
//这里只是定义了规则并没有使用
delegate void MyFun();
//委托规则的申明是不能重名(同一语句块中)
//表示用来装载或传递返回值为int 有一个int参数的函数的委托容器规则
public delegate int MyFun02(int a);

4、自定义委托的应用

  • 委托变量是函数的容器
  • 作为类的成员
//委托基本使用方法
//专门用来装载函数的容器

//无参委托两种赋值调用方法
MyFun f = new MyFun(Fun);
f.Invoke();//调用
MyFun f2 = Fun;
f2();//调用

//有参委托两种赋值调用方法
MyFun02 f4 = new MyFun02(Fun02);
f4.Invoke(4);
MyFun02 f3 = Fun02;
Console.WriteLine(f3(1));

//****************************方法
//无参无返回值函数
static void Fun()
{
   Console.WriteLine("123456");
}
//有参有返回值函数
static int Fun02(int index)
{
   return index;          
}
  • 作为函数的参数
  • 作为类的成员、作为函数的参数、添加 增 方法和 删 方法
class Test
        {
            //作为类的成员
            public MyFun fun;
            public MyFun02 fun2;

            //作为函数的参数
            public void TestFun(MyFun fun,MyFun02 fun02)
            {
                //先处理一些别的逻辑当这些逻辑处理完了再执行传入的函数
                int i = 1;
                i *= 2;
                
                //直接执行
                //fun();
                //fun02(i);
                
                //先存起来一会儿在执行
                this.fun = fun;
                this.fun2 = fun02;
            }
             //增
            public void AddFun(MyFun fun,MyFun02 fun2)
            {
                this.fun += fun;
                this.fun2 += fun2;
            }

            //删
            public void RemoFun(MyFun fun,MyFun02 fun2)
            {
                this.fun -= fun;
                this.fun2 -= fun2;
            }
        }
//***************************调用
Test test = new Test();//实例化
test.TestFun(Fun,Fun02);//调用委托方法
Console.WriteLine(test.fun2(2));//打印

5、委托变量可以存储多个函数

  • 使用委托存储多个函数
//如何使用委托存储多个函数
MyFun ff = Fun;
//使用+=之前必须已经赋值过
ff += Fun3;
ff();//调用

//从容器中移除指定函数
//如果容器里没有被减参数,就不执行
ff -= Fun;
ff();

//清空容器,注意完全清空后继续调用会报错
ff = null;
if (ff != null)
{
    ff();
}

//**************************方法
static void Fun3()
{
   Console.WriteLine("bbbbbb");
}
  • 调用在委托里写的增删方法存储多个函数
Test t = new Test();
t.AddFun(Fun,Fun02);//增
t.fun();//调用
t.fun02(13);

t.RemoFun(Fun,Fun02)//删

6、系统定义好的委托

  • 使用系统自带委托需要引用using System;
//系统自带委托 无参无返回值的委托
Action action = new Action(Fun);
action += Fun3;
action();

//系统自带的泛型委托 有指定返回值
Func<string> funcString = Fun4;
string a = funcString();
Console.WriteLine(a);
Func<int> funcInt = Fun5;

//可以传多个参数的委托,系统提供了16个参数委托的重载
Action<int, string> action1 = Fun6;

//可以传多个参数的泛型委托,系统提供了16个参数委托的重载
Func<int, int> func2 = Fun02;

//************************************方法
static string Fun4()
{
     return "abc";
}
static int Fun5()
{
     return 2;
}
static void Fun6(int a,string b)
{
   Console.WriteLine("is");
}

  • 可以为自定义的委托添加泛型
  • 委托是支持泛型的可以让返回值和参数可变更方便我们的使用
delegate T Myfun3<T, k>(T v, k k);

7、总结

  • 简单理解委托就是装载、传递函数的容器而已
  • 可以用委托变量来存储函数或者传递函数的
  • 系统其实已经提供了很多委托给我们用
  • Action:没有返回值,参数提供了0~16个委托给我们用
  • Func:有返回值,参数提供了0~16个委托给我们用

C#事件

1、什么是事件

  • 事件是基于委托的存在
  • 事件是委托的安全包裹
  • 让委托的使用更具有安全性
  • 事件是—种特殊的变量类型

2、事件的使用

  • 语法:访问修饰符 event 委托类型 事件名;
  • 事件的使用:
  • 1.事件是作为成员变量存在于类中
  • 2.委托怎么用事件就怎么用
  • 事件相对于委托的区别:
  • 1.不能在类外部 赋值
  • 2.不能再类外部 调用
	/// <summary>
    /// 事件
    /// </summary>
    class Test
    {
        //委托成员变量用于存储函数的
        public Action myFun;
        //事件成员变量用于存储函数
        public event Action myEvent;

        public Test()
        {
            //事件的使用和委托一模一样只是有些细微的区别
            myFun = TestFun;
            myFun += TestFun;
            myFun -= TestFun;
            myFun();
            myFun.Invoke();
            myFun = null;

            Console.WriteLine("**************");

            myEvent = TestFun;
            myEvent += TestFun;
            myEvent -= TestFun;
            myEvent();
            myEvent.Invoke();
            myEvent = null;
        }		
		//类中封装的事件方法,让事件在外部可以被调用
        public void DoEvent()
        {
            if (myEvent != null)
            {
                myEvent();
            }          
        }

        public void TestFun()
        {
            Console.WriteLine("aaa");
        }
    }

    internal class Program
    {
        static void Main()
        {
            Test t = new Test();
            //委托可以在外部赋值
            t.myFun = null;
            t.myFun = TestFun;
            t.myFun = t.myFun + TestFun;
            
            //事件是不能再外部赋值的
            //t.myEvent = null;//报错
            //t.myEvent = TestFun;//报错
            //虽然不能直接赋值但是可以加减去添加移除记录的函数
            //t.myEvent = t.myEvent + TestFun;//报错
            t.myEvent += TestFun;
            t.myEvent -= TestFun;

            //委托是可以在外部调用的
            t.myFun();
            t.myFun.Invoke();
            //事件不能再外部调用
            //t.myEvent();//报错

            //如果事件想要在外部调用事件,必须在类中为其封装方法
            t.DoEvent();

            Action a = TestFun;
            //事件是不能作为临时变量在函数中使用的
            //event Action b = TestFun;//报错
        }

        static void TestFun()
        {

        }
    }
  • 注意:它只能作为成员存在于类和接口以及结构体中

3、事件的作用

  • 1.防止外部随意置空委托
  • 2.防止外营随意调用委托
  • 3.事件相当于对委托进行了一次封装让其更加安全

4、总结

  • 事件和委托的区别:
  • 事件和委托的使用基本是一模一样的
  • 事件就是特殊的委托
  • 主要区别:
  • 1.事件不能再外部使用赋值=符号,只能使用+–委托哪里都能用
  • 2.事件不能再外部执行委托哪里都能执行
  • 3.事件不能作为函数中的临时变量的委托可以

匿名函数

1、什么是匿名函数

  • 顾名思义,就是没有名字的函数
  • 匿名函数的使用主要是配合委托和事件进行使用
  • 脱离委托和事件是不会使用匿名函数的

2、基本语法

delegate (参数列表)
{
	//函数逻辑
}
//1.函数中传递委托参数时使用
//2.委托或事件赋值时使用

3、匿名函数的使用

//1、无参无返回值匿名函数
//声明匿名函数,还没调用
//这个委托容器什么时候调用,这个匿名函数就什么时候调用
Action test = delegate ()
{
     Console.WriteLine("匿名函数逻辑");
};
test();

//2、有参匿名函数
Action<int,string> test2 = delegate (int a,string b)
{
    Console.WriteLine(a);
    Console.WriteLine(b);
};
test2(10, "aaa");

//3、有返回值的匿名函数
Func<string> test3 = delegate ()
{
    return "123";
};          
Console.WriteLine(test3());

  • 4、一般情况会作为函数参数传递或者作为函数返回值
Test t = new Test();
//t.Dosmthing(100, Fun);//需要传参
//传递参数
t.Dosmthing(100, delegate ()//使用匿名函数
{
   Console.WriteLine("随参数传入的匿名函数逻辑");
});
//Action ac = delegate ()
//{
//    Console.WriteLine("随参数传入的匿名函数逻辑");
//};
//t.Dosmthing(10, ac);

//返回值
Action ac2 = t.GetFun();
ac2();
//一步到位直接调用返回的委托函数
t.GetFun()();

//添加到委托或事件容器中后不记录无法单独移除
Action ac3 = delegate ()
{
   Console.WriteLine("匿名函数一");
};
ac3 += delegate ()
{
    Console.WriteLine("匿名函数二");
};
ac3();
//因为匿名函数没有名字所以没有办法指定移除某一个匿名函数
//此匿名函数非彼匿名函数 不能通过看逻辑是否一样就证明是一个
ac3 -= delegate ()//无效
{
    Console.WriteLine("匿名函数二");
};
//唯一清除方法
ac3 = null;
//******************************************方法
static void Fun()
{
    Console.WriteLine("外部传入的函数逻辑");
}
//****************************************Test类
    //委托
    class Test
    {
        public Action action;
        //作为参数传递时
        public void Dosmthing(int a,Action fun)
        {
            Console.WriteLine(a);
            fun();
        }

        //作为返回值
        public Action GetFun()
        {
            //return Test01;
            return delegate ()
            {
                Console.WriteLine("函数内部返回的一个匿名函数逻辑");
            };
        }

        //无参无返回值
        public void Test01()
        {
            Console.WriteLine("逻辑");
        }
    }

4、总结

1、主要是在委托传递和存储时为了方便可以直接使用匿名函数

2、缺点: 添加到委托或事件容器中后不记录无法单独移除


Lambad表达式

1、什么是Lambad表达式

  • 可以将lambad表达式 理解为匿名函数的简写
  • 它除了写法不同外,使用上和匿名函数一模一样
  • 都是和委托或者事件 配合使用的

2、语法

//lambad表达式语法
(参数列表)=>
{ 
    //函数体
}

3、使用

  • 使用上也和匿名函数相似
//1、无参无返回值
Action test=() =>
{
    Console.WriteLine("无参无返回值的lambad表达式");
};
test();

//2、有参
Action<int > test2 = (int index) =>
{
     Console.WriteLine("有参lambad表达式{0}",index);
};
test2(2);

//3、甚至参数类型都可以省略参数类型和委托或事件容器一致
Action<int> test3 = (index) =>
{
     Console.WriteLine("省略参数类型的写法{0}", index);
};
test3(3);

//4、有返回值
Func<string, int> test4 = (index) =>
{
   Console.WriteLine("有返回值的lambad表达式{0}",index);
    return 1;
};
Console.WriteLine(test4("123"));
。。。

4、闭包

  • 内层的函数可以引用包含在它外层的函数的变量
  • 即使外层函数的执行已经终止
  • 注意:该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值
class Test
    {
        public event Action action;
        public Test()
        {
            int value = 10;
            //这里就形成了闭包
            //因为当构造函数执行完毕时其中串明的临时变量value的声明周期被改变了
            action = () =>
            {
                Console.WriteLine(value);
            };
            for (int i = 0; i < 10; i++)
            {
                //每次循环重新赋值
                int a = i;
                action += () =>
                {
                    //打印的都是10,而不是按顺序打印1-10
                    //Console.WriteLine(i);

                    //如果想要打印1-10,需每次循环时重新赋值
                    Console.WriteLine(a);
                };
            }
        }
        //定义外部可调用事件方法
        public void DoSomthing()
        {
            action();
        }
    }
//***********************************调用
Test test1 = new Test();
test1.DoSomthing();

5、总结

  • 匿名函数的特殊写法就是lambad表达式
  • 参数列表可以直接省略参数类型
  • 主要在委托传递和存储时为了方便可以直接使用匿名函数或者lambad表达式
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风不停息丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值