当前位置: 首页 > news >正文

C# 事件

目录

    • 1、事件模型的5个组成部分
    • 2、使用内置委托类型声明事件
      • 2.1 `EventHandler`
        • 2.1.1 ?
        • 2.1.2 this
        • 2.1.3 使用匿名函数和lamda表达式
          • 2.1.3.1 匿名函数
          • 2.1.3.2 lamda表达式
        • 2.1.4 异常处理
      • 2.2 `EventHandler<TEventArgs>`
    • 3、使用自定义委托类型声明事件
      • 3.1 事件的完整声明
      • 3.2 事件的简化声明
    • 4、委托和事件的区别
    • 5、事件的作用

事件(event)让类或对象具备在某件感兴趣的事发生时通知其他类或对象的能力,触发事件的类(让事件发生的类)叫做发布者(publisher),处理事件的类叫做订阅者(subscriber)。
在这里插入图片描述
举个例子,闻鸡起舞,这个成语中有两个对象,鸡和祖逖。凌晨1-3点,鸡开始第一次的啼叫,祖逖听到鸡叫声立马起床开始舞剑。鸡是事件的发布者,当时间到了凌晨1-3点时,鸡叫事件发生了,而祖逖的行为和鸡叫这个事件之间存在着订阅关系(祖逖在心里告诉自己,只要我听到鸡叫声了,就赶紧起床舞剑)。因此,鸡叫时间一发生,就会向祖逖发出通知信号,祖逖接收到了信号,就会采取相应的行动。

需要注意的是:

  • 1.祖逖的行为和鸡叫这个事件之间需要有订阅关系。也就是说,因为有订阅关系,所以鸡叫的发生才会让祖逖舞剑。而如果是狗叫,祖逖并不会因此舞剑。
  • 2.一个发布者–>多个订阅者。不光是祖逖听到鸡叫会起床舞剑,邻居家听到鸡叫也可以采取相应行动,比如赶集。
  • 3.多个发布者–>一个订阅者。祖逖不光听到自家的鸡叫起床舞剑,听到别人家的鸡叫也会起床舞剑。

在这里插入图片描述

1、事件模型的5个组成部分

  • 事件的拥有者(event source)。也就是Publisher。
  • 事件成员(event)。事件是隶属于发布者的,比如公司上市这个事件是隶属于公司主体的,没有公司何谈公司上市?
  • 事件的订阅者(响应者)。也就是Subscriber。
  • 事件处理器(event handler)。是订阅者的一个成员函数,本质上是一个回调函数。所谓函数,就是用来执行特定功能的代码块;而回调函数,就是在程序需要的时候会去调用,不需要的时候不去调用的函数。当Publisher没有向Subscriber发通知时,回调函数不会执行,一旦Publisher向Subscriber发通知,回调函数立马执行。
  • 事件订阅(subscribe)。将事件和事件处理器联系起来,本质上是一种以委托类型为基础的约定。

2、使用内置委托类型声明事件

.net framework中提供了内置委托类型EventHandlerEventHandler<TEventArgs>EventHandler用来声明不包含事件参数的事件,EventHandler<TEventArgs>用来声明包含事件参数的事件。事件参数是事件发生时,发布者会向订阅者发送的数据。比如闻鸡起舞这个事件的发生,鸡并没有向祖逖发送任何信息,发送了也听不懂哈哈,因为鸡叫本身已经能说明所有问题,不需要额外的信息了。再比如手机收到信息了,手机铃声响这个事件会向人发送通知的同时还会发送信息(事件参数),铃声只是提醒人看手机,信息才是关键,所以在这个事件中需要有事件参数。

2.1 EventHandler

namespace ConsoleApp1
{class Program{static void Main(string[] args){Chicken chicken = new Chicken();People zuDi = new People();chicken.Crow += zuDi.Action;chicken.StartCrow();Console.ReadLine();}}class Chicken{public event EventHandler Crow;public void StartCrow(){Console.WriteLine("koke-kokko");OnCrow(EventArgs.Empty);}protected virtual void OnCrow(EventArgs e){Crow?.Invoke(this,e);}}class People{public void Action(object sender, EventArgs e){Console.WriteLine("I get up.");Console.WriteLine("I started practicing my sword.");}}
}/*
Outputs:
koke-kokko
I get up.
I started practicing my sword.
*/

这个例子是对闻鸡起舞的实现,Chicken类是对发布者的实现,包含使用event关键字声明的内置委托类型的事件,以及触发事件。

  • +=:建立事件和事件处理器之间的订阅关系。
  • -=:取消事件和事件处理器之间的订阅关系。

为什么要取消事件和事件处理器之间的订阅关系?防止内存泄漏。事件订阅者持有对事件发布者的引用,当订阅者订阅了一个事件时,事件发布者会将订阅者添加到其事件订阅者列表中。这意味着,只要事件发布者存在,订阅者对象就不会被垃圾回收器回收,即使订阅者对象本身不再被其他部分的代码引用。
在这里插入图片描述

比如,在这样一个软件中,有很多可以点击的按钮,每个按钮点击后会产生不同的效果。整个窗体就是一个发布者,而每个按钮是一个订阅者,如果我们每次点击完按钮产生相应效果后,不取消按钮和窗体之间的订阅关系,那么随着我们点击的按钮越来越多,内存中需要存储越来越多这样的订阅关系,而这种订阅关系并不会自动清除掉,除非我们将软件关闭,也就是发布者被干掉了。因此不及时取消订阅很容易导致内存泄漏。

Crow?.Invoke(this,e);

着重说一下这行代码,为什么要加?,以及传入的参数this有什么用?

2.1.1 ?

这里的?是可空类型修饰符,考虑一种情况:将事件处理器订阅事件的这行代码注释掉,然后将可空类型修饰符删掉,这个时候再运行代码。

在这里插入图片描述

在这里插入图片描述

会报错,“未将对象引用设置到对象的实例”,我们知道,Invoke方法是委托用来间接调用函数的(C# 委托),由于我们将chicken.Crow += zuDi.Action;注释掉了,也就是说,委托是null,没有引用到任何实例对象,就会报错了。当没有任何的事件处理器订阅事件时,处于代码的健壮性考虑,我们会加上可空类型修饰符,避免出现这种报错。通过使用?,C#在调用Invoke方法之前,会先检查Crow是否为空,如果为空,就不会去调用Invoke方法,从而避免了空引用异常。

在这里插入图片描述我们也可以显式地对Crow是否为空进行判断。

if(Crow != null)
{Crow.Invoke(this, e);
}
2.1.2 this
namespace ConsoleApp1
{class Program{static void Main(string[] args){Chicken chicken = new Chicken();chicken.Name = "BigChicken";People zuDi = new People();chicken.Crow += zuDi.Action;chicken.StartCrow();Console.ReadLine();}}class Chicken{public event EventHandler Crow;public string Name { get; set; }public void StartCrow(){Console.WriteLine("koke-kokko");OnCrow(EventArgs.Empty);}protected virtual void OnCrow(EventArgs e){Crow?.Invoke(this, e);}}class People{public void Action(object sender, EventArgs e){if(sender != null){Chicken chicken = sender as Chicken;Console.WriteLine("I hear the {0} is crowing.",chicken.Name);Console.WriteLine("I get up.");Console.WriteLine("I started practicing my sword.");}}}
}/*
Outputs:
koke-kokko
I hear the BigChicken is crowing.
I get up.
I started practicing my sword.
*/

this代表的是Chicken的实例。假设这样一种情况,祖逖现在只有在听到BigChicken这一只鸡叫,才会起床舞剑,也就是说,任何其他的鸡叫,祖逖都不会做出反应,那么这个时候,就需要能在事件处理器中判断是哪一个对象触发的事件。sender是对this的引用。
在这里插入图片描述

2.1.3 使用匿名函数和lamda表达式
2.1.3.1 匿名函数

为了使程序更简洁,我们可以使用匿名函数或lamda表达式来完成订阅者的功能。

namespace ConsoleApp1
{class Program{static void Main(string[] args){Chicken chicken = new Chicken();chicken.Crow += delegate (object sender, EventArgs e){Console.WriteLine("I get up.");Console.WriteLine("I started practicing my sword.");};chicken.StartCrow();Console.ReadLine();}}class Chicken{public event EventHandler Crow;public void StartCrow(){Console.WriteLine("koke-kokko");OnCrow(EventArgs.Empty);}protected virtual void OnCrow(EventArgs e){Crow?.Invoke(this, e);}}
}
2.1.3.2 lamda表达式
namespace ConsoleApp1
{class Program{static void Main(string[] args){Chicken chicken = new Chicken();chicken.Crow += (sneder, e) =>{Console.WriteLine("I get up.");Console.WriteLine("I started practicing my sword.");};chicken.StartCrow();Console.ReadLine();}}class Chicken{public event EventHandler Crow;public void StartCrow(){Console.WriteLine("koke-kokko");OnCrow(EventArgs.Empty);}protected virtual void OnCrow(EventArgs e){Crow?.Invoke(this, e);}}
}
2.1.4 异常处理
namespace ConsoleApp1
{class Program{static void Main(string[] args){Bell bell = new Bell();Teather teather = new Teather();Student student = new Student();bell.Ring += teather.Action;bell.Ring += student.Action;bell.BellRing();Console.ReadLine();}}class Bell{public event EventHandler Ring;public void BellRing(){Console.WriteLine("Ding!Dang!");OnRing(EventArgs.Empty);}protected virtual void OnRing(EventArgs e){Ring?.Invoke(this,e);}}class Teather{public void Action(object sender, EventArgs e){Console.WriteLine("I walked into the classroom.");}}class Student{public void Action(object sender, EventArgs e){Console.WriteLine("I settled into my seat.");}}
}/*Outputs:
Ding!Dang!
I walked into the classroom.
I settled into my seat.
*/

上课铃声响起,老师走进教室,学生在座位上坐好。这个例子中一个事件的发布者对应着两个订阅者。如果老师这个订阅者发生异常,我们来看看程序的运行结果。
在这里插入图片描述
程序直接抛出异常,这就意味着,不仅老师的事件处理函数无法执行,学生的事件处理函数也无法执行。
当多个订阅者订阅发布者的消息时,一个订阅者出现错误,会导致后面所有的订阅者都无法接收到消息,因此为了避免这种情况的发生,需要进行异常处理。

namespace ConsoleApp1
{class Program{static void Main(string[] args){Bell bell = new Bell();Teather teather = new Teather();Student student = new Student();bell.Ring += teather.Action;bell.Ring += student.Action;bell.BellRing();Console.ReadLine();}}class Bell{public event EventHandler Ring;public void BellRing(){Console.WriteLine("Ding!Dang!");OnRing(EventArgs.Empty);}protected virtual void OnRing(EventArgs e){Ring?.Invoke(this, e);}}class Teather{public void Action(object sender, EventArgs e){try{//Console.WriteLine("I walked into the classroom.");throw new Exception("Teacher throw an exception.");}catch (Exception ex){Console.WriteLine("Exception caught: {0}", ex.Message);}}}class Student{public void Action(object sender, EventArgs e){try{Console.WriteLine("I settled into my seat.");}catch (Exception ex){Console.WriteLine("Exception caught: {0}", ex.Message);}}}
}/*Outputs:
Ding!Dang!
Exception caught: Teacher throw an exception.
I settled into my seat.
*/

这样,哪怕老师的事件处理函数抛出异常,也不会影响学生的事件处理函数正常执行。

2.2 EventHandler<TEventArgs>

namespace ConsoleApp1
{class Program{static void Main(string[] args){Phone phone = new Phone();People people = new People();phone.MessageReceived += people.Action;phone.ReceiveMessage();Console.ReadLine();}}class MessageReceivedEventArgs:EventArgs{public string Message { get; }public MessageReceivedEventArgs(string message){Message = message;}}class Phone{public event EventHandler<MessageReceivedEventArgs> MessageReceived;public void ReceiveMessage(){Console.WriteLine("Ding,Ding,Ding.");MessageReceivedEventArgs message = new MessageReceivedEventArgs("Hello,World!");OnMessageReceived(message);}protected virtual void OnMessageReceived(MessageReceivedEventArgs e){MessageReceived?.Invoke(this, e);}}class People{public void Action(object sender, MessageReceivedEventArgs e){string message = e.Message;Console.WriteLine("I receive the mesage.");Console.WriteLine("The message is: {0}", message);}}
}/*Outputs:
Ding,Ding,Ding.
I receive the mesage.
The message is: Hello,World!
*/

3、使用自定义委托类型声明事件

3.1 事件的完整声明

namespace ConsoleApp1
{public delegate void DingEventHandler(Phone phone, DingEventArgs e);public class DingEventArgs:EventArgs{public string Message { get; set; }}class Program{static void Main(string[] args){Phone phone = new Phone();People people = new People();phone.Ding += new DingEventHandler(people.Action);DingEventArgs e = new DingEventArgs();e.Message = "Hello,World!";phone.MessageReceived(e);Console.ReadLine();}}public class Phone{private DingEventHandler dingEventHandler;public string ID { get; }public Phone(){ID = "123456";}public event DingEventHandler Ding{add{this.dingEventHandler += value;}remove{this.dingEventHandler -= value;}}public void MessageReceived(DingEventArgs e){Console.WriteLine("Ding,Ding!");Console.WriteLine("Here comes the message.");OnDing(e);}protected virtual void OnDing(DingEventArgs e){this.dingEventHandler?.Invoke(this, e);}}public class People{public void Action(Phone phone, DingEventArgs e){string id = phone.ID;string message = e.Message;Console.WriteLine("I receive message: [{0}], from {1}",message,id);}}
}/*Outputs:
Ding,Ding!
Here comes the message.
I receive message: [Hello,World!], from 123456
*/

以上是事件声明的完整形式,它能够让我们理解事件运行的本质原理。都说事件是基于委托的,那委托究竟在事件中是怎么发挥作用的呢?
我们知道,委托相当于C++中的函数指针,也就是对函数的引用,我们可以通过调用委托实例的方法,来间接调用委托所引用的函数,而不是直接去调用函数,这样做的好处是,可以将函数封装在委托中,把函数以参数的形式进行传递,也可以将函数赋值给一个变量。参见C# 委托。

public delegate void DingEventHandler(Phone phone, DingEventArgs e);

这行代码定义了一个委托类型。

private DingEventHandler dingEventHandler;

这行代码定义了一个委托类型的私有字段。

public event DingEventHandler Ding
{add{this.dingEventHandler += value;}remove{this.dingEventHandler -= value;}
}

这段代码是对事件的定义,使用event关键字定义一个委托类型的事件。通过addremove访问器,来将委托和函数绑定在一起,以及取消绑定,value是上下文关键字,代表传进来的函数(方法)。

// this.dingEventHandler += value;
this.dingEventHandler += new DingEventHandler(value);

其实,下面的代码才是完整的表达方式,这一步的过程是:将value所代表的函数封装到DingEventHandler委托中。
在这里插入图片描述

this.dingEventHandler?.Invoke(this, e);

这一行代码是通过委托实例的Invoke方法,来间接调用它所引用的函数。

phone.Ding += new DingEventHandler(people.Action);

这一行代码看似是将函数和事件建立联系,实际上是将函数封装到委托中。
还有一点值得一提,委托的签名必须和函数的签名保持一致。

3.2 事件的简化声明

namespace ConsoleApp1
{public delegate void DingEventHandler(Phone phone, DingEventArgs e);public class DingEventArgs:EventArgs{public string Message { get; set; }}class Program{static void Main(string[] args){Phone phone = new Phone();People people = new People();phone.Ding += people.Action;DingEventArgs e = new DingEventArgs();e.Message = "Hello,World!";phone.MessageReceived(e);Console.ReadLine();}}public class Phone{public string ID { get; }public Phone(){ID = "123456";}public event DingEventHandler Ding;public void MessageReceived(DingEventArgs e){Console.WriteLine("Ding,Ding!");Console.WriteLine("Here comes the message.");OnDing(e);}protected virtual void OnDing(DingEventArgs e){Ding?.Invoke(this, e);}}public class People{public void Action(Phone phone, DingEventArgs e){string id = phone.ID;string message = e.Message;Console.WriteLine("I receive message: [{0}], from {1}",message,id);}}
}

事件的简化声明省略了委托字段的显式定义,以及addremove访问器。实际上,尽管我们没有显式地将这些代码写出来,但是编译器在背后仍然会生成这些代码。
我们使用C#的反编译器看看具体情况。
将程序丢进去。

在这里插入图片描述
在这里插入图片描述

可见,反编译的结果中包含了私有的委托字段,以及add和remove访问器。
所以,事件的简化声明是C#为我们提供的语法糖,它让我们能够以更简单的方式、更少的代码来声明事件。但是如果我们不了解事件的完整声明的话,我们很容易误以为事件就是一个委托类型的字段,实际上并不是。

4、委托和事件的区别

委托和事件的关系,类似于字段和属性的关系。C# 类(二)——成员:字段、属性、方法、事件。

namespace ConsoleApp1
{class Program{static void Main(string[] args){Student stu = new Student();stu.Age = 15;Console.WriteLine(stu.Age);Console.ReadLine();}}class Student{private int age;public int Age { get; set; }}
}

我们知道,出于数据安全的考虑,字段的访问级别我们一般设置为private,也就是只可以在类内访问,而不能在类外访问,在类外通过访问属性来间接访问字段。属性将字段进行封装,保护了数据安全。实际上,如果我们把字段的访问级别改为public,然后删掉属性,并不改变代码的运行结果,但是会给代码带来潜在的风险。

class Student
{public int age;
}

同样地,事件也是对委托的一种封装,事件的引入不允许在类外直接访问委托类型的字段(private),而只能通过事件(public)来间接访问委托。

在这里插入图片描述

namespace ConsoleApp1
{class Program{static void Main(string[] args){Bell bell = new Bell();Teather teather = new Teather();Student student = new Student();bell.Ring += teather.Action;bell.Ring += student.Action;bell.BellRing();Console.ReadLine();}}class Bell{public EventHandler Ring;public void BellRing(){Console.WriteLine("Ding!Dang!");OnRing(EventArgs.Empty);}protected virtual void OnRing(EventArgs e){Ring?.Invoke(this, e);}}class Teather{public void Action(object sender, EventArgs e){try{throw new Exception("Teacher throw an exception.");}catch (Exception ex){Console.WriteLine("Exception caught: {0}", ex.Message);}}}class Student{public void Action(object sender, EventArgs e){try{Console.WriteLine("I settled into my seat.");}catch (Exception ex){Console.WriteLine("Exception caught: {0}", ex.Message);}}}
}

将代码中的event关键字删掉,代码功能由原来的委托类型事件的声明,变成了委托类型字段的声明。再次运行代码,发现运行结果并不会改变。这样的改动将委托直接暴露在了类外,给程序带来了潜在的风险。

在这里插入图片描述
在这里插入图片描述

比如,我们可以在类外直接调用委托的Invoke方法,这样的结果是,铃声还没有响,老师就走进教室了,学生也需要在位置上坐好,这就不符合代码逻辑了。

在这里插入图片描述

而如果我们使用的是事件的话,在类外根本就不允许有这种操作,这个时候我们想要触发事件,只能通过调用bell.BellRing();方法,而如果我们想直接通过事件来调用Invoke方法,编译器会报错事件"Bell.Ring"只能出现在+=或-=的左边,编译器压根就不允许我们有这种操作。

5、事件的作用

事件的封装性使得事件的触发逻辑和订阅者列表被封装在类内部,外部代码不能直接调用事件,也不能直接访问事件的订阅者列表,从而提高了代码的安全性和稳定性。

  • 松耦合:程序设计的一大原则是“高内聚,低耦合”,事件让发布者和订阅者之间以一种松耦合的方式联系起来,发布者不需要知道订阅者是谁,订阅者也不需要知道发布者是谁,只要订阅者的事件处理函数签名满足发布者中委托类型字段的签名,就可以形成订阅关系。
  • 灵活性:以动态调用和可扩展的方式对一件事情做出响应。动态调用指的是,当感兴趣的事情发生时,事件被触发,发布者通知订阅者采取行动;而如果感兴趣的事情没有发生,那么发布者和订阅者都保持沉默状态。可扩展指的是,我们如果想要增加订阅者列表,无需对发布者代码进行任何修改,只需要确保事件处理函数签名正确即可。
  • 发布者-订阅者模式。

相关文章:

C# 事件

目录 1、事件模型的5个组成部分2、使用内置委托类型声明事件2.1 EventHandler2.1.1 &#xff1f;2.1.2 this2.1.3 使用匿名函数和lamda表达式2.1.3.1 匿名函数2.1.3.2 lamda表达式 2.1.4 异常处理 2.2 EventHandler<TEventArgs> 3、使用自定义委托类型声明事件3.1 事件的…...

WebRtc02: WebRtc架构、目录结构、运行机制

整体架构 WebRtc主要分为三层&#xff1a; CAPI层&#xff1a;外层调用Session管理核心层&#xff1a;包括视频引擎、音频引擎、网络传输 可由使用者重写视频引擎&#xff1a;编解码器、视频缓存、视频增强音频引擎&#xff1a;编解码器、音频缓存、回音消除、降噪传输&#x…...

耐高压26V输入5V升压充电8.4V芯片

HU6877作为一款集成了26V高耐压保护的5V升压至8.4V两节锂电池充电管理IC&#xff0c;凭借其高效升压、智能充电管理、多重安全保护及高耐压特性&#xff0c;在高端手电筒、便携式医疗设备、无人机等领域展现出了广泛的应用前景。本文将详细探讨HU6877的技术特点、工作原理、应用…...

【连续学习之LwM算法】2019年CVPR顶会论文:Learning without memorizing

1 介绍 年份&#xff1a;2019 期刊&#xff1a; 2019CVPR 引用量&#xff1a;611 Dhar P, Singh R V, Peng K C, et al. Learning without memorizing[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2019: 5138-5146. 本文提…...

数组和指针

数组和指针 数组初始化特殊的字符数组 指针的引入指针的定义和初始化指针操作指针和const限定符 C风格字符串 C提供了两种类似于vector和迭代器的低级复合类型&#xff1a;数组和指针。与vector类似&#xff0c;数组也可以保存某种类型的一组对象&#xff0c;只是数组一经创建就…...

【数电尾灯设计】2022-8-16

缘由数电尾灯设计问题&#xff0c;求解答--CSDN问答 从题目可以列出 000 100 010 111-----------4进制 000 100 010 110 001 101 011 111-----------8进制 由列出可知用16进制芯片的3个引脚可以获得8进制推导出4进制从而可用逻辑处理为4进制实现尾灯功能。之上第一步实现了尾灯…...

F.interpolate函数

F.interpolate 是 PyTorch 中用于对张量&#xff08;通常是图像数据&#xff09;进行插值操作的函数&#xff0c;常用于调整张量的大小&#xff0c;例如改变图像的分辨率。它支持多种插值方法&#xff0c;包括最近邻插值、双线性插值和三次插值等。 语法 torch.nn.functional…...

Tableau数据可视化与仪表盘搭建-基础图表制作

目录 对比分析&#xff1a;比大小 柱状图 条形图 数据钻取 筛选器 热力图 气泡图 变化分析&#xff1a;看趋势 折线图 预测 面积图 关系分布&#xff1a;看位置 散点图 直方图 地图 构成分析&#xff1a;看占比 饼图 树地图 堆积图 对比分析&#xff1a;比大…...

Sentinel-5P遥感数据下载及预处理教程【20250105】

Sentinel-5P是欧空局&#xff08;Europe Space Agency&#xff0c;ESA&#xff09;于2017年10月13日发射的一颗全球大气污染监测卫星。卫星搭载了对流层观测仪&#xff08;Tropospheric Monitoring Instrument&#xff0c;TROPOMI&#xff09;&#xff0c;可以有效的观测全球各…...

快速学习 pytest 基础知识

全篇大概 5000 字&#xff08;含代码&#xff09;&#xff0c;建议阅读时间10min 简介 Pytest是一个非常成熟的测试框架&#xff0c;适用于但愿测试、UI测试、接口测试。 简单灵活、上手快支持参数化具有多个第三方插件可以直接使用 assert 进行断言 一、Pytest安装 pip inst…...

[python]解决AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘问题

环境&#xff1a;python3.13.1 问题&#xff1a; 利用ddddocr进行验证码识别时报AttributeError: module PIL.Image has no attribute ANTIALIAS错误信息&#xff0c;具体如下&#xff1a; python3 Python 3.13.1 (main, Jan 6 2025, 22:29:09) [Clang 16.0.0 (clang-1600.…...

【C++】类和对象(下):友元、static成员、内部类、explicit 和 匿名对象

文章目录 前言一、友元二、static成员三、内部类四、隐式类型转换&#xff08;加explicit可以阻止隐式类型转换&#xff09;五、匿名对象 前言 一、友元&#xff08;友元函数 和 友元类&#xff09; 二、static成员&#xff08;类中被static修饰的成员变量 和 成员函数&#xf…...

Shapelet-aeon-1

本文中&#xff0c;我们将首创一个长度为m、维度为d的时间序列表示为一个向量。 在aeon中&#xff0c;我们将一个序列描述为x:(n_channels, n_timepoints). x是一个二维数组&#xff0c;其中n_channels对应的是维度d&#xff0c;或者说是特征数量&#xff1b;n_timepoints对应…...

2. 进程和线程

文章目录 前言1. 进程是什么2. 进程的相关属性3. 线程是什么4. 为什么引入线程5. 进程和线程的区别 前言 上一篇博客&#xff0c;我们讲到了CPU和操作系统&#xff0c;今天我们讲一个操作系统中一个非常重要的概念—线程和进程 1. 进程是什么 每个应用程序运行于现代操作系统…...

CE中注册的符号地址如何通过编程获取

我的方式是先执行lua申请共享内存&#xff0c;内存名称是进程id&#xff0c;这样多开也不受影响&#xff0c;然后通过共享内存的名字就可以读到地址了。之后的人造指针的地址也都可以放这里集中管理。 -- 申请内存 local size 1024 -- 申请 1024 字节&#xff08;1 KB&#…...

QT上实现SVM进行数据分类

针对不了解SVM的原理的同学强推下面这个课程&#xff1a; 6.机器学习课程&#xff08;六&#xff09;支持向量机&#xff08;线性模型&#xff09;问题_哔哩哔哩_bilibili 一、QT实现SVM的方法 1.调用SVM的C语言库&#xff1a;麻烦&#xff0c;要专门去找库&#xff0c;cmak…...

【unity调用c++动态库,c++和c#相互调用】

文章目录 前言一、编写C++动态库(MyLibrary.cpp)二、 编译C++动态库三、编写C#代码(CallbackExample.cs)四、将C++动态库与Unity项目集成五、Unity运行结果六、注意事项总结前言 在Unity中调用C++动态库并进行回调到C#的功能实现,涉及到C++与C#之间的交互。这个过程的关键…...

SAP FICO财务模块的会计年度变式是什么?有特殊的年度期间(财年)吗?可以不按日历月份来设定会计期间吗?

文章目录 一、会计准则中关于会计期间的一般性原则二、SAP系统中关于会计期间的设定&#xff08;1&#xff09;一般性的期间设定方式&#xff08;2&#xff09;特殊期间是什么&#xff1f;&#xff08;3&#xff09;有些国家与众不同的财年 三、可以不按日历月份来设定会计期间…...

Linux(17)——使用 DNF 安装和更新软件包

目录 一、使用 DNF 管理软件包&#xff1a; 1、 DNF 查找软件&#xff1a; 2、DNF 安装软件&#xff1a; 3、DNF 删除软件&#xff1a; 二、使用 DNF 管理软件包组&#xff1a; 1、DNF 显示组信息&#xff1a; 2、DNF 安装组&#xff1a; 三、使用 DNF 查看事务历史记录…...

vue中的设计模式

vue中使用了哪些设计模式 1. 观察者模式&#xff08;Observer Pattern&#xff09; 应用场景&#xff1a;Vue 的响应式系统核心就是观察者模式。 实现方式&#xff1a;通过 Object.defineProperty 或 Proxy 监听数据变化&#xff0c;当数据发生变化时&#xff0c;通知依赖的视…...

大模型性能随笔

1. 如果开了accumulate gradient更新&#xff0c;则pipeline并行的bubble会变小很多。因为每foward好多个batch&#xff0c;才开始backward。 2. chat聊天任务上&#xff0c;可以把prefix KV cache reuse功能利用起来&#xff1b;然后把同一个session的对话&#xff0c;路由到…...

pdf预览 报:Failed to load module script

pdf 预览报&#xff1a; Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “application/octet-stream”. Strict MIME type checking is enforced for module scripts per HTML spec. 报错原因&#xff1a…...

基于 gitlab-runner 实现调度GPU的资源

本篇目录 1. 客户需求2. 需求调研3. 实践3.1 方案一&#xff1a;环境变量的方式3.2 方案二&#xff1a;k8s 自身的spec注入机制 4. 效果 该实践来自于客户的一个真实需求 1. 客户需求 客户的某些流水线需要使用GPU资源&#xff0c;但是对于GPU服务器而言&#xff0c;会有多张G…...

【源码+文档+调试讲解】项目申报小程序

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代…...

【ShuQiHere】使用 SCP 进行安全文件传输

【ShuQiHere】&#x1f680; 在日常的开发和运维工作中&#xff0c;文件传输是一个常见的任务。scp&#xff08;Secure Copy&#xff09;是一个基于 SSH 协议的文件传输工具&#xff0c;能够在本地和远程主机之间安全地复制文件和目录。本文将详细介绍 scp 的使用方法&#xf…...

算法基础 - 二分查找

文章目录 二分查找算法通常应用于已排序的数组。以下是一个C实现的二分查找算法示例&#xff1a; #include <iostream> #include <vector>int binarySearch(const std::vector<int>& nums, int target) {int left 0;int right nums.size() - 1;while …...

权限掩码umask

1 、 设置新建文件或目录的默认权限 在 Linux 系统中&#xff0c;当用户创建一个新的文件或目录时&#xff0c;系统都会为新建的文件或目录分配默认的权限&#xff0c;该默认权限与umask 值有关&#xff0c;其具体关系是&#xff1a; 新建文件的默认权限 0666-umask 值 新建…...

5. CSS引入方式

5.1 CSS的三种样式 按照 CSS 样式书写的位置(或者引入的方式)&#xff0c;CSS样式表可以分为三大类&#xff1a; 1.行内样式表&#xff08;行内式&#xff09; 2.内部样式表&#xff08;嵌入式&#xff09; 3. 外部样式表&#xff08;链接式&#xff09; 5.2 内部样式表 …...

在Linux中,SElinux的作用是什么?如何临时和永久的更改SElinux上下文?

SELinux在Linux系统中扮演者至关重要的安全角色&#xff0c;它通过实施强制访问控制&#xff08;Mandatory Access Control,MAC&#xff09;策略来增强系统的安全性。不同于传统的用户和组权限管理机制&#xff08;即自主访问控制DAC&#xff09;&#xff0c;selinux提供了一种…...

windows下,golang+vscode+delve 远程调试

1 现在远程服务器安装golang和delve golang的安装&#xff0c;通过官网直接下载安装包安装接口 go install github.com/go-delve/delve/cmd/dlvlatest 如果dlv和golang版本不匹配&#xff0c;这里把latest换成匹配的版本&#xff0c;比如1.20.0 2 编译带调试信息的程序 go bu…...

社交牛杂症?锂电系统有了DeviceNet转Profinet网关后,沟通无障碍

在锂电行业蓬勃发展的当下&#xff0c;自动化与智能化浪潮正席卷而来&#xff0c;这无疑对设备间的通信精准度与流畅性提出了严苛要求&#xff0c;而稳联技术Devicenet转Profinet网&#xff08;WL-PN-DVNM&#xff09;关恰是破局的关键利器。 此网关设备犹如一座桥梁&#xff0…...

C语言的正则表达式

C语言中的正则表达式 引言 正则表达式是一种用于描述字符串模式的工具&#xff0c;它可以用来进行字符串匹配、查找、替换等操作。在编程中&#xff0c;正则表达式被广泛应用于数据验证、信息提取等场景。C语言虽然没有内置的正则表达式支持&#xff0c;但通过一些库我们同样…...

Launcher3主页面加载显示流程分析

布局结构 抓取布局后&#xff0c;可以看到每个图标是一个DoubleShadowBubbleTextView&#xff0c;父布局是CellLayout、workspace。 我们可以在CellLayout添加子view打印出调用堆栈信息&#xff0c;可以整体上看页面加载显示流程。 主要类 Launcher.java&#xff1a;主界面&…...

机器学习算法---贝叶斯学习

1.了解相关概念 先验概率&#xff1a;有数据集d,以及假设h,此时h是不确定的。在还没有训练数据之前h的初始概率记为P(h),类似地我们把P(d)表示训练数据d在任何假设都未知或不确定时的概率。P(d|h)表示已知假设h成立时d的概率。 后验概率&#xff1a;就是在数据d上经过学习之后…...

PyCharm+RobotFramework框架实现UDS自动化测试——(二)RobotFramework环境配置

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者&#xff0c;时光不负有心人。 文章目录 1.环境准配2.Pycharm中相关配置2.1. 安装Hyper RobotFramework Support 3.脚本执行环境3.1 执行单条的配置3.2 执行全部用例配置 4.工程运行4.1 单条用例运行4.…...

colnames看似简单,却能优化数据处理流程

引言 在数据处理和分析中&#xff0c;变量名称是至关重要的&#xff0c;它们决定了数据的可读性和操作的简便性。在R语言中&#xff0c;colnames 函数以其简单的语法设计&#xff0c;提供了高效管理数据框列名的能力&#xff0c;尤其是在复杂的爬虫任务中显得尤为重要。本篇文…...

Spring Boot整合Minio实现文件上传

Spring Boot整合Minio后&#xff0c;前端的文件上传有两种方式&#xff1a; 文件上传到后端&#xff0c;由后端保存到Minio 这种方式好处是完全由后端集中管理&#xff0c;可以很好的做到、身份验证、权限控制、文件与处理等&#xff0c;并且可以做一些额外的业务逻辑&#xf…...

GitHub - riscv-software-src/riscv-isa-sim: Spike, a RISC-V ISA Simulator

GitHub - riscv-software-src/riscv-isa-sim: Spike, a RISC-V ISA Simulator 操作手册 $ apt-get install device-tree-compiler libboost-regex-dev libboost-system-dev $ mkdir build $ cd build $ ../configure --prefix$RISCV $ make $ [sudo] make install 具体安装 …...

ceph文件系统

ceph文件系统&#xff1a; 使用设备&#xff1a;4台机器 高度可扩展&#xff0c;分布式的存储文件系统&#xff0c;旨在提供高性能&#xff0c;高可靠性和高可用的对象存储&#xff0c;块存储&#xff0c;文件系统的存储 使用分布式的算法保证数据的高可用和一致性 ceph的架…...

模型创新、论文复现、科研辅导、论文代码定制

建模先锋团队长期致力于为用户提供优质的代码定制服务。团队提供全网最低价格的服务&#xff0c;同时保证高性价比和高质量的代码交付&#xff0c;为您提供个性化定制的服务。 以下是定制服务范围&#xff1a; 通过深度学习和信号处理技术&#xff0c;我们能够针对不同行业和场…...

【flink-cdc】flink-cdc 3版本debug启动pipeline任务,mysql-doris

官方文档 github仓库地址 Flink cdc debug调试动态变更表结构 经过测试使用&#xff0c;在启动任务配置Modify classpath添加jar的方式&#xff0c;容易出错classNotFoundException等等。 一、build project flink-cdc版本&#xff1a;3.2.1 mvn clean package "-Dma…...

mybatisX插件的使用,以及打包成配置

装mybatisX插件&#xff1b; idea连接数据库&#xff1b; 点击mybatisx-generator&#xff0c;设置自己装mybatisX插件&#xff1b; idea连接数据库&#xff1b; 点击mybatisx-generator&#xff0c;设置自己要的包和类&#xff1b; 如果要把自己的配置设置成一个自定义模板&a…...

pip下载包出现SSLError

报错&#xff1a; ERROR: Could not install packages due to an OSError: HTTPSConnectionPool(host‘files.pythonhosted.org’, port443): Max retries exceeded with url: /packages/8a/c2/ae7227e4b089c6a8210920db9d5ac59186b0a84eb1e6d96b9218916cdaf1/taming_transform…...

Linux下查看文件和文件夹占用空间大小

使用Linux命令&#xff0c;查看文件磁盘所占的空间大小&#xff0c;下面可以通过以下命令进行操作 df 可以查看一级文件夹大小、使用比例、档案系统及其挂入点&#xff0c;但对文件束手无策du 查看文件和文件夹的磁盘使用空间 在使用中&#xff0c;一般是df命令和du命令一起联…...

【论文+源码】基于Spring和Spring MVC的汉服文化宣传网站

为了实现一个基于Spring和Spring MVC的汉服文化宣传网站,我们需要创建一个简单的Web应用程序来展示汉服文化和相关信息。这个系统将包括以下几个部分: 数据库表设计:定义文章、用户和评论的相关表。实体类:表示数据库中的数据。DAO层接口及MyBatis映射文件:用于与数据库交…...

C++语言的学习路线

C语言的学习路线 C是一门复杂而强大的编程语言&#xff0c;由于其高性能和灵活性&#xff0c;受到了许多开发者和企业的青睐。无论是系统软件、嵌入式系统还是游戏开发&#xff0c;C都有非常广泛的应用。要掌握C这门语言&#xff0c;需要合理制定学习路线&#xff0c;并结合实…...

【OpenCV】使用Python和OpenCV实现火焰检测

1、 项目源码和结构&#xff08;转&#xff09; https://github.com/mushfiq1998/fire-detection-python-opencv 2、 运行环境 # 安装playsound&#xff1a;用于播放报警声音 pip install playsound # 安装opencv-python&#xff1a;cv2用于图像和视频处理&#xff0c;特别是…...

【ArcGISPro/GeoScenePro】解决常见的空间参考和投影问题

修复空间参考缺失的图像 数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 查看属性坐标 查看属性范围 范围值并不是零或接近于零。 这意味着栅格具有范围,因此其已正确进行...

Ruby语言的语法

Ruby语言的语法之美 Ruby是一种动态、开放源代码的编程语言&#xff0c;由日本的松本行弘&#xff08;Yukihiro Matsumoto&#xff09;于1995年首次发布。Ruby语言以其简洁、优雅和易于阅读的语法而闻名。它不仅适合初学者&#xff0c;还被广泛应用于Web开发、数据分析和其他领…...

概述(讲讲python基本语法和第三方库)

我是北子&#xff0c;这是我自己写的python教程&#xff0c;主要是记录自己的学习成果方便自己日后复习&#xff0c; 我先学了C/C&#xff0c;所以这套教程中可能会将很多概念和C/C去对比&#xff0c;所以该教程大概不适合零基础的人。 it seems that python nowadays 只在人工…...