C#基础之委托,事件
文章目录
- 1 委托
- 1.1 简介
- 1.2 操作使用
- 1.2.1 声明委托(Delegate)
- 1.2.2 实例化委托(Delegate)
- 1.2.3 直接调用和invoke
- 1.2.4 Invoke 和 BeginInvoke
- 1.3 委托的多播
- 1.4 委托的匿名和lambda
- 1.4.1 匿名方法
- 1.4.2 lambda 表达式
- 1.5 内置委托
- 1.5.1 Action系列
- 1.5.2 Func 系列
- 1.5.3 Predicate
- 1.6 示例
- 2 事件
- 2.1 简介
- 2.2 原理
- 2.2.1 讲解
- 2.2.2 add 和 remove 访问器
- 2.3 使用原生委托
- 2.4 自定义委托
- 2.4.1 声明
- 2.4.2 操作
- 2.4.2.1 示例一
- 2.4.2.2 示例二
1 委托
1.1 简介
C# 中的委托(Delegate
)类似于 C 或 C++ 中函数的指针。委托(Delegate
) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托(Delegate
)特别用于实现事件
和回调
方法。所有的委托(Delegate
)都派生自 System.Delegate
类。
1.2 操作使用
1.2.1 声明委托(Delegate)
委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。
例如,假设有一个委托:public delegate int MyDelegate (string s);
上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量
声明委托的语法如下:
delegate <return type> <delegate-name> <parameter list>
1.2.2 实例化委托(Delegate)
一旦声明了委托类型,委托对象必须使用 new
关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new
语句的参数就像方法调用一样书写,但是不带有参数
。例如:
public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
下面的实例演示了委托的声明、实例化和使用,该委托可用于引用带有一个整型参数的方法,并返回一个整型值。
using System;delegate int NumberChanger(int n);
namespace DelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}static void Main(string[] args){// 创建委托实例NumberChanger nc1 = new NumberChanger(AddNum);NumberChanger nc2 = new NumberChanger(MultNum);// 使用委托对象调用方法nc1(25);Console.WriteLine("Value of Num: {0}", getNum());nc2(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}结果:
Value of Num: 35
Value of Num: 175
注意
:调用委托对应方法一般是通过invoke
方法,但是从 C# 2.0
开始,委托的调用可以直接使用方法调用语法,而不需要显式调用 Invoke
方法。
1.2.3 直接调用和invoke
虽然委托调用底层实际上是通过 Invoke
方法实现的,但语法上允许直接调用委托,就像调用普通方法一样。换句话说,调用委托
和直接调用 Invoke
方法是等效的。
假设我们有一个 Action 类型的委托:
Action action = () => Console.WriteLine("Hello, Delegate!");直接调用委托:
action(); // 输出: Hello, Delegate!通过 Invoke 方法调用:
action.Invoke(); // 输出: Hello, Delegate!
两种方式的结果完全一样,因为 ()
是对委托对象 Invoke
方法的简化语法糖
。
为什么允许直接调用?
简洁性
:如果每次调用都必须写 .Invoke,代码显得冗长。因此,C# 提供了直接调用语法,增强代码可读性。语法糖
:编译器在编译时会自动将直接调用委托语法转换为 Invoke 方法的调用。
即:action();
实际被编译为:action.Invoke();
- 优先推荐直接调用
直接调用的方式更加简洁可读,因此在大多数情况下,推荐使用action()
而不是显式调用action.Invoke()
。
为什么保留 Invoke 方法?虽然直接调用语法更方便,但在某些特殊场景下,显式调用 Invoke 方法可能更合适:
反射场景
:通过反射调用委托时,需要使用Invoke
方法。动态场景
:在动态生成代码或动态委托时,Invoke
方法更明确。
using System;
using System.Reflection;class Program
{static void Main(){Action action = PrintMessage;// 使用反射调用 InvokeMethodInfo invokeMethod = action.GetType().GetMethod("Invoke");invokeMethod.Invoke(action, null);}static void PrintMessage(){Console.WriteLine("Hello, Reflection!");}
}
输出:
Hello, Reflection!
1.2.4 Invoke 和 BeginInvoke
委托的 Invoke
和 BeginInvoke
方法分别用于同步
和异步
调用委托。它们的主要区别体现在调用方式、线程管理和返回结果的处理上。
Invoke 和 BeginInvoke 的区别
特性 | Invoke | BeginInvoke |
---|---|---|
调用类型 | 同步调用 | 异步调用 |
线程阻塞 | 当前线程会阻塞,直到方法执行完成 | 当前线程不会阻塞 |
返回结果 | 直接返回方法的返回值 | 返回 IAsyncResult 对象,通过 EndInvoke 获取返回值 |
异常处理 | 异常会直接在调用线程中抛出 | 异常在调用 EndInvoke 时抛出 |
线程使用 | 在调用线程上执行方法 | 在线程池中执行方法 |
使用场景 | 方法较快且调用线程不能被中断时 | 方法较慢且需要异步执行时 |
- Invoke:同步调用
定义:Invoke
是同步调用,当前线程会等待方法执行完毕后再继续执行后续代码。
特点:- 阻塞调用:调用线程会被阻塞,直到被调用的方法完成。
- 返回结果:直接返回被调用方法的返回值(如果有)。
- 异常处理:如果被调用的方法抛出异常,异常会在调用线程中传播。
// 定义一个委托
delegate int AddDelegate(int x, int y);AddDelegate add = (x, y) => x + y;// 同步调用
int result = add.Invoke(3, 4);
Console.WriteLine($"Result: {result}"); // 输出:Result: 7
- BeginInvoke:异步调用
定义:BeginInvoke
是异步调用,立即返回一个 IAsyncResult 对象,并不会阻塞调用线程。
特点:- 非阻塞调用:调用线程可以继续执行其他代码,而被调用的方法在后台线程中执行。
- 回调机制:可以通过传递回调方法或轮询 IAsyncResult 对象来获取结果。
需要显式调用 EndInvoke 方法以获取结果或处理异常。
// 定义一个委托
delegate int AddDelegate(int x, int y);
AddDelegate add = (x, y) =>
{Console.WriteLine("Adding...");System.Threading.Thread.Sleep(2000); // 模拟耗时操作return x + y;
};// 异步调用
IAsyncResult asyncResult = add.BeginInvoke(3, 4, null, null);
// 主线程继续执行其他任务
Console.WriteLine("Doing other work...");
// 获取异步调用结果
int result = add.EndInvoke(asyncResult);
Console.WriteLine($"Result: {result}"); // 输出:Result: 7
BeginInvoke 的回调,可以通过回调函数在异步操作完成后处理结果:
void CallbackMethod(IAsyncResult ar)
{// 获取委托实例AddDelegate add = (AddDelegate)ar.AsyncState;// 获取结果int result = add.EndInvoke(ar);Console.WriteLine($"Result in Callback: {result}");
}AddDelegate add = (x, y) =>
{Console.WriteLine("Adding...");System.Threading.Thread.Sleep(2000);return x + y;
};// 异步调用并指定回调函数
add.BeginInvoke(5, 7, CallbackMethod, add);
// 主线程继续工作
Console.WriteLine("Doing other work...");
注意事项:
- BeginInvoke 使用线程池中的线程来执行方法,因此需要注意线程池的资源消耗。
必须调用 EndInvoke: - 调用 BeginInvoke 后,无论是否需要结果,都必须调用 EndInvoke,否则可能会导致资源泄漏。
- 推荐使用 Task 和 async/await:
- 在现代 C# 中,推荐使用 Task 和
async/await
替代BeginInvoke
和EndInvoke
,因为它们更易读且不易出错。
1.3 委托的多播
委托对象可使用 +
运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。-
运算符可用于从合并的委托中移除组件委托。
使用委托的这个有用的特点,可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting)
,也叫组播
。
+
和 -
运算符确实可以直接用于委托对象的合并和移除,但这和 +=
和 -=
的用法有所不同。它们的区别主要在于运算场景
和赋值方式
。具体来说
+
和-
运算符:用于直接创建新的委托对象,不影响原始委托。它们不会修改原始委托,而是生成一个新的多播委托对象。- 使用
+
合并两个委托对象,生成一个新的多播委托。 - 使用
-
从多播委托中移除一个委托,生成一个新的委托对象。
- 使用
+=
和-=
运算符:用于修改已有的委托实例,直接在原始变量上添加或移除委托。+=
将一个委托添加到现有委托链上,结果赋给原变量。-=
从现有委托链中移除一个委托,结果赋给原变量。
下面的程序演示了委托的多播:
using System;delegate int NumberChanger(int n);
namespace DelegateAppl
{class TestDelegate{static int num = 10;public static int AddNum(int p){num += p;return num;}public static int MultNum(int q){num *= q;return num;}public static int getNum(){return num;}static void Main(string[] args){// 创建委托实例NumberChanger nc;NumberChanger nc1 = new NumberChanger(AddNum);NumberChanger nc2 = new NumberChanger(MultNum);nc = nc1;nc += nc2;// 调用多播nc(5);Console.WriteLine("Value of Num: {0}", getNum());Console.ReadKey();}}
}结果:
Value of Num: 75
注意
:
在C#中,当使用+=
操作符向委托添加方法时,有两种方式是等效的:
- 显式地创建一个新的委托实例并将其添加到现有的委托链中
myDelegate += new NumberChanger(AddNum); - 省略new 部分,直接添加方法。
C#
编译器会自动为您处理委托的实例化(如果必要的话):myDelegate += AddNum
这两种方式在功能上是完全相同的。从C# 2.0开始,第二种方式(省略new关键字和委托类型)变得更加流行,因为它更简洁,并且减少了不必要的代码。
1.4 委托的匿名和lambda
二者比较:
特性 | 匿名方法 (delegate) | Lambda 表达式 |
---|---|---|
语法简洁性 | 较繁琐,需要显式写出 delegate 关键字和参数列表 | 更简洁,直接用 (参数) => {} 表达逻辑 |
表达式形式支持 | 不支持表达式形式,必须用 {} 包裹逻辑块 | 支持表达式形式,单行逻辑可以省略 {} 和 return |
捕获外部变量(闭包) | 支持 | 支持 |
语法风格 | 更接近传统 C# 方法声明 | 更现代、函数式编程风格 |
语义清晰性 | delegate 明确表明它是匿名方法 | 使用 => 运算符,强调简洁和函数式思想 |
1.4.1 匿名方法
匿名方法是通过使用 delegate
关键字创建委托实例来声明的。
语法
delegate(parameters) { statement; }
例如:
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{Console.WriteLine("Anonymous Method: {0}", x);
};
代码块 Console.WriteLine("Anonymous Method: {0}", x);
是匿名方法的主体。
委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。
using System;delegate void NumberChanger(int n);
namespace DelegateAppl
{class TestDelegate{static int num = 10;public static void AddNum(int p){num += p;Console.WriteLine("Named Method: {0}", num);}public static void MultNum(int q){num *= q;Console.WriteLine("Named Method: {0}", num);}static void Main(string[] args){// 使用匿名方法创建委托实例NumberChanger nc = delegate(int x){Console.WriteLine("Anonymous Method: {0}", x);};// 使用匿名方法调用委托nc(10);// 使用命名方法实例化委托nc = new NumberChanger(AddNum);// 使用命名方法调用委托nc(5);// 使用另一个命名方法实例化委托nc = new NumberChanger(MultNum);// 使用命名方法调用委托nc(2);Console.ReadKey();}}
}
1.4.2 lambda 表达式
在 C# 2.0 及更高版本中,引入了 lambda 表达式,它是一种更简洁的语法形式,用于编写匿名方法。并且 从 C# 2.0
开始对委托的实例化做了简化,委托类型的实例化在某些情况下可以省略显式使用 new
关键字
使用 lambda 表达式:
using System;delegate void NumberChanger(int n);namespace DelegateAppl
{class TestDelegate{static int num = 10;public static void AddNum(int p){num += p;Console.WriteLine("Named Method: {0}", num);}public static void MultNum(int q){num *= q;Console.WriteLine("Named Method: {0}", num);}static void Main(string[] args){// 使用 lambda 表达式创建委托实例NumberChanger nc = x => Console.WriteLine($"Lambda Expression: {x}");// 使用 lambda 表达式调用委托nc(10);// 使用命名方法实例化委托nc = new NumberChanger(AddNum);// 使用命名方法调用委托nc(5);// 使用另一个命名方法实例化委托nc = new NumberChanger(MultNum);// 使用命名方法调用委托nc(2);Console.ReadKey();}}
}
1.5 内置委托
C# 提供了一些内置的泛型委托,可以覆盖大部分常见场景,主要包括以下几个
1.5.1 Action系列
Action
是一个用于定义没有返回值的方法的委托。支持最多 16 个参数的重载。
Action action = () => Console.WriteLine("No parameters");
action();Action<int, string> actionWithParams = (x, y) => Console.WriteLine($"x: {x}, y: {y}");
actionWithParams(10, "hello");
1.5.2 Func 系列
Func
是一个带有返回值的泛型委托。最多支持 16 个输入参数,最后一个泛型参数是返回值的类型,前面的泛型参数表示输入参数
Func<int, int, int> add = (x, y) => x + y;
int result = add(3, 5);
Console.WriteLine(result); // 输出 8
1.5.3 Predicate
Predicate<T>
是一个返回 bool
的泛型委托,常用于过滤或条件判断。
Predicate<int> isEven = x => x % 2 == 0;
bool check = isEven(4);
Console.WriteLine(check); // 输出 True
1.6 示例
下面的实例演示了委托的用法。委托 printString 可用于引用带有一个字符串作为输入的方法,并不返回任何东西。
我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件:
using System;
using System.IO;namespace DelegateAppl
{class PrintString{static FileStream fs;static StreamWriter sw;// 委托声明public delegate void printString(string s);// 该方法打印到控制台public static void WriteToScreen(string str){Console.WriteLine("The String is: {0}", str);}// 该方法打印到文件public static void WriteToFile(string s){fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);sw = new StreamWriter(fs);sw.WriteLine(s);sw.Flush();sw.Close();fs.Close();}// 该方法把委托作为参数,并使用它调用方法public static void sendString(printString ps){ps("Hello World");}static void Main(string[] args){printString ps1 = new printString(WriteToScreen);printString ps2 = new printString(WriteToFile);sendString(ps1);sendString(ps2);Console.ReadKey();}}
}结果:
The String is: Hello World
2 事件
2.1 简介
C# 事件(Event
)是一种成员,用于将特定的事件通知发送给订阅者。事件通常用于实现观察者模式
,它允许一个对象将状态的变化通知其他对象,而不需要知道这些对象的细节。
事件(Event
) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。
C# 中使用事件机制实现线程间的通信。
关键点:
- 声明委托:定义事件将使用的委托类型。委托是一个函数签名。
- 声明事件:使用
event
关键字声明一个事件。 - 触发事件:在适当的时候调用事件,通知所有订阅者。
- 订阅和取消订阅事件:其他类可以通过
+=
和-=
运算符订阅和取消订阅事件。
事件模型五个组成部分:
- 事件的拥有者
- 事件成员
- 事件的响应者
- 事件处理器
- 事件订阅–把事件处理器与事件关联在一起,本质是一种以委托类型为基础的
2.2 原理
2.2.1 讲解
事件在类中声明且生成,且通过使用同一个类或其他类中的委托
与事件
处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher
) 类。其他接受该事件的类被称为 订阅器(subscriber
) 类。事件使用 发布-订阅(publisher-subscriber
) 模型。
- 发布器(
publisher
) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器类的对象调用这个事件,并通知其他的对象。 - 订阅器(
subscriber
) 是一个接受事件并提供事件处理程序的对象。在发布器类中的委托调用订阅器(subscriber
)类中的方法(事件处理程序)
在C#中,通常使用+=
操作符来订阅事件,使用-=
操作符来取消订阅事件
2.2.2 add 和 remove 访问器
自己定义事件的 add
和 remove
访问器,从而控制事件订阅和取消订阅的具体行为。
下面的 EventHandler 是系统自带 事件,不用声明
public class EventDemo
{private EventHandler _myEvent;// 自定义事件public event EventHandler MyEvent{add{Console.WriteLine("Adding a subscriber");_myEvent += value;}remove{Console.WriteLine("Removing a subscriber");_myEvent -= value;}}public void TriggerEvent(){_myEvent?.Invoke(this, EventArgs.Empty);}
}class Program
{static void Main(){EventDemo demo = new EventDemo();EventHandler handler = (sender, e) => Console.WriteLine("Event triggered!");// 订阅事件demo.MyEvent += handler; // 输出: Adding a subscriber// 触发事件demo.TriggerEvent(); // 输出: Event triggered!// 取消订阅事件demo.MyEvent -= handler; // 输出: Removing a subscriber}
}
自定义 add 和 remove 访问器通常在以下场景中使用:
- 自定义订阅逻辑:需要记录订阅者或对订阅者进行筛选时。
- 线程安全:确保事件的订阅和取消订阅在多线程环境下安全。
- 限制订阅数量:控制最多只能有特定数量的订阅者。
- 日志记录或调试:每次事件订阅或取消时记录相关信息。
注意事项:
- 事件是委托的包装:事件是基于委托的,但它对委托的直接访问进行了限制,提供了一种更安全的机制来管理委托调用。
- 事件默认行为:如果不需要特殊逻辑,直接使用默认的 add 和 remove,即可满足大部分场景。
- 不要直接对事件赋值:只能通过
+=
和-=
访问事件。直接赋值(如 MyEvent = null)是不允许的,除非是在声明类内部。
2.3 使用原生委托
namespace EventExample
{class Program{MyForm form = new MyForm();// 写此处原生对应的 事件可以先写此处名字,让visualstudio 自动生成对应参数类型的 事件form.Click += form.FormClicked;form.ShowDialog();}class MyForm : Form{internal void FormClicked(object sender,EventArgs e){this.Text = DataTime.Now.ToString();}}
}
2.4 自定义委托
2.4.1 声明
在类的内部声明事件,首先必须声明该事件的委托类型。
例如:
public delegate void BoilerLogHandler(string status);
然后,声明事件本身,使用 event
关键字:
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
上面的代码定义了一个名为 BoilerLogHandler 的委托和一个名为 BoilerEventLog 的事件,该事件在生成的时候会调用委托。
2.4.2 操作
2.4.2.1 示例一
以下示例展示了如何在 C# 中使用事件:
using System;
namespace EventDemo
{// 定义一个委托类型,用于事件处理程序public delegate void NotifyEventHandler(object sender, EventArgs e);// 发布者类public class ProcessBusinessLogic{// 声明事件public event NotifyEventHandler ProcessCompleted;// 触发事件的方法protected virtual void OnProcessCompleted(EventArgs e){ProcessCompleted?.Invoke(this, e);}// 模拟业务逻辑过程并触发事件public void StartProcess(){Console.WriteLine("Process Started!");// 这里可以加入实际的业务逻辑// 业务逻辑完成,触发事件OnProcessCompleted(EventArgs.Empty);}}// 订阅者类public class EventSubscriber{public void Subscribe(ProcessBusinessLogic process){process.ProcessCompleted += Process_ProcessCompleted;}private void Process_ProcessCompleted(object sender, EventArgs e){Console.WriteLine("Process Completed!");}}class Program{static void Main(string[] args){ProcessBusinessLogic process = new ProcessBusinessLogic();EventSubscriber subscriber = new EventSubscriber();// 订阅事件subscriber.Subscribe(process);// 启动过程process.StartProcess();Console.ReadLine();}}
}
说明
- 定义委托类型:
public delegate void NotifyEventHandler(object sender, EventArgs e);
这是一个委托类型,它定义了事件处理程序的签名。通常使用EventHandler
或EventHandler<TEventArgs>
来替代自定义的委托。 - 声明事件:
public event NotifyEventHandler ProcessCompleted;
这是一个使用 NotifyEventHandler 委托类型的事件。 - 触发事件:
protected virtual void OnProcessCompleted(EventArgs e)
{ProcessCompleted?.Invoke(this, e);
}
这是一个受保护的方法,用于触发事件。使用 ?.Invoke
语法来确保只有在有订阅者时才调用事件。
- 订阅和取消订阅事件:
process.ProcessCompleted += Process_ProcessCompleted;
订阅者使用+=
运算符订阅事件,并定义事件处理程序 Process_ProcessCompleted。
2.4.2.2 示例二
using System;
namespace SimpleEvent
{using System;/***********发布器类***********/public class EventTest{private int value;public delegate void NumManipulationHandler();public event NumManipulationHandler ChangeNum;protected virtual void OnNumChanged(){if ( ChangeNum != null ){ChangeNum(); /* 事件被触发 */}else {Console.WriteLine( "event not fire" );Console.ReadKey(); /* 回车继续 */}}public EventTest(){int n = 5;SetValue( n );}public void SetValue( int n ){if ( value != n ){value = n;OnNumChanged();}}}/***********订阅器类***********/public class subscribEvent{public void printf(){Console.WriteLine( "event fire" );Console.ReadKey(); /* 回车继续 */}}/***********触发***********/public class MainClass{public static void Main(){EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */subscribEvent v = new subscribEvent(); /* 实例化对象 */e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */e.SetValue( 7 );e.SetValue( 11 );}}
}结果:
event not fire
event fire
event fire
相关文章:
C#基础之委托,事件
文章目录 1 委托1.1 简介1.2 操作使用1.2.1 声明委托(Delegate)1.2.2 实例化委托(Delegate)1.2.3 直接调用和invoke1.2.4 Invoke 和 BeginInvoke 1.3 委托的多播1.4 委托的匿名和lambda1.4.1 匿名方法1.4.2 lambda 表达式 1.5 内置…...
nginx配置静态资源的访问
比如静态资源图片位于/mnt/software/nginx/html/static/images目录下,那么nginx.conf中的配置则为: # 静态文件目录 location /static/images/ { root /mnt/software/nginx/html; try_files $uri $uri/ 404; #找不到时提示404 …...

JS的魔法三角:constructor、prototype与__proto__
在JavaScript中,constructor、prototype和__proto__是与对象创建和继承机制紧密相关的三个概念。理解它们之间的关系对于掌握JavaScript的面向对象编程至关重要。下面将详细介绍这个魔法三角: 1. constructor 定义:constructor是一个函数&am…...

CA系统(file.h---申请认证的处理)
#pragma once #ifndef FILEMANAGER_H #define FILEMANAGER_H #include <string> namespace F_ile {// 读取文件,返回文件内容bool readFilename(const std::string& filePath);bool readFilePubilcpath(const std::string& filePath);bool getNameFro…...

matlab显示sin二维图
1,新建脚本 2、保存脚本 3、脚本命令:clc 清除 脚本命令的信息 clrear all 清除全部 4工作区内容:变量啥的 x0:0.001:2*pi%% 开始 精度 中值 ysin(x) y1cos(x) figure%%产生一个屏幕 plot(x,y)%%打印坐标 title(ysin(x))%%标题 xlabel(…...
验证 kubelet 服务已经停止并且不再生成错误日志
要验证 kubelet 服务已经停止并且不再生成错误日志,可以按照以下步骤进行操作: 1. 检查 kubelet 服务状态 首先,确认 kubelet 服务已经停止。 Bash 深色版本 sudo systemctl status kubelet 你应该看到类似以下的输出,表示服…...

【Linux】进程控制-----进程替换
目录 一、为什么要进行进程替换: 二、进程替换的原理: 三、exec家族: 1、execl: 2、execlp: 3、execv: 4、execvp: 5、execle和execve 编辑 putenv: 一、为什么要进行进程…...

安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本
安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本。 原因是:当前操作系统版本为Windows Server 2016 Standard版本,其自带的Microsoft .NET Framework 版本为4.6太低,不满足要求。 根据报错的提示,点击链接…...

使用ECharts创建带百分比标注的环形图
在数据可视化领域,环形图是一种非常有效的图表类型,它能够清晰地展示各部分与整体的关系。今天,我们将通过ECharts来创建一个带百分比标注的环形图,并详细解释如何实现这一效果。 1. 数据准备 首先,我们定义了一些基础…...

学习threejs,设置envMap环境贴图创建反光效果
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.CubeTextureLoader 立…...
go语言里的mkdir mkdirall有什么区别?
在Go语言中,os.Mkdir 和 os.MkdirAll 都是用来创建目录的函数,但它们之间存在一些关键的区别。 ### os.Mkdir - **功能**:os.Mkdir 用于创建一个单一的目录。如果该目录已经存在,则会返回一个错误。 - **参数**: - na…...

使用Python OpenCV实现图像形状检测
目录 一、环境准备 二、读取和预处理图像 读取图像 灰度化 滤波去噪 三、边缘检测 四、查找轮廓 五、绘制轮廓 六、形状分类 七、显示结果 八、完整代码示例 九、总结 图像形状检测是计算机视觉领域中的一项关键技术,广泛应用于工业自动化、机器人视觉、医学图像处…...
继上一篇,设置弹框次数以及自适应图片弹框,部分机型(vivo)老手机不显示的问题
上一篇写的本来测试好多型号都无事, 今天下午公司的战斗机vivo横空冒出… 晕 弹框直接显示都出不来了,现在还有用这种老的机型的,但是没办法咯~ 前端遇到这种兼容性的问题就要勇于解决 主要解决了这几点: // 添加图片加载事件 <imgv-if"imageUrl":src"image…...

基于RISC-V 的代理内核实验(使用ub虚拟机安装基本环境)
1.安装支撑软件 第一步,安装依赖库 RISC-V交叉编译器的执行仍然需要一些本地支撑软件包,可使用以下命令安装: $ sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bis…...
【MMKV】HarmonyOS中的优秀轻量化存储方式
MMKV 引言 在移动应用开发的世界里,数据存储和管理是至关重要的一环。随着技术的不断进步,开发者们对于高性能、轻量级、易用的数据存储解决方案的需求日益增长。MMKV(Memory Mapped Key-Value)正是这样一个开源的高性能key-val…...

docker安装hadoop环境
一、使用docker搭建基础镜像 1、拉取centos系统镜像 # 我这里使用centos7为例子 docker pull centos:7 2、创建一个dockerfiler文件,用来构建自定义一个有ssh功能的centos镜像 # 基础镜像 FROM centos:7 # 作者 #MAINTAINER hadoop ADD Centos-7.repo /etc/yum.re…...

开源多媒体处理工具ffmpeg是什么?如何安装?使用ffmpeg将M3U8格式转换为MP4
目录 一、FFmpeg是什么二、安装FFmpeg(windows)三、将M3U8格式转换为MP4格式 一、FFmpeg是什么 FFmpeg是一款非常强大的开源多媒体处理工具,它几乎可以处理所有类型的视频、音频、字幕以及相关的元数据。 FFmpeg的主要用途包括但不限于&…...
算法刷题Day5: BM52 数组中只出现一次的两个数字
描述: 一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 要求:空间复杂度 O(1),时间复杂度O(n)。 题目传送门 is here 思路: 方法一:最简单的思路就…...

55 基于单片机的方波频率可调
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 采用STC89C52单片机最小系统,设计DAC0832、放大器、与示波器显示方波,四位数码管显示频率,两个按键可调。 二、硬件资源 基于KEIL5编写C代码,PROT…...
23.useUnload
在 Web 应用开发中,处理页面卸载(unload)事件是一个重要但常常被忽视的方面。无论是提醒用户保存未保存的更改,还是执行一些清理操作,都需要在用户即将离开页面时进行处理。useUnload 钩子提供了一种简洁的方式来在 React 组件中处理 beforeunload 事件,使得在用户试图关…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...

Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...

【51单片机】4. 模块化编程与LCD1602Debug
1. 什么是模块化编程 传统编程会将所有函数放在main.c中,如果使用的模块多,一个文件内会有很多代码,不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数声明,其他.c文…...
StarRocks 全面向量化执行引擎深度解析
StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计,相比传统行式处理引擎(如MySQL),性能可提升 5-10倍。以下是分层拆解: 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...