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

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

委托的 InvokeBeginInvoke 方法分别用于同步异步调用委托。它们的主要区别体现在调用方式、线程管理和返回结果的处理上。

Invoke 和 BeginInvoke 的区别

特性InvokeBeginInvoke
调用类型同步调用异步调用
线程阻塞当前线程会阻塞,直到方法执行完成当前线程不会阻塞
返回结果直接返回方法的返回值返回 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 替代 BeginInvokeEndInvoke,因为它们更易读且不易出错。

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 访问器

自己定义事件的 addremove 访问器,从而控制事件订阅和取消订阅的具体行为。
下面的 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);
    这是一个委托类型,它定义了事件处理程序的签名。通常使用 EventHandlerEventHandler<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 声明委托&#xff08;Delegate&#xff09;1.2.2 实例化委托&#xff08;Delegate&#xff09;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目录下&#xff0c;那么nginx.conf中的配置则为&#xff1a; # 静态文件目录 location /static/images/ { root /mnt/software/nginx/html; try_files $uri $uri/ 404; #找不到时提示404 …...

JS的魔法三角:constructor、prototype与__proto__

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

CA系统(file.h---申请认证的处理)

#pragma once #ifndef FILEMANAGER_H #define FILEMANAGER_H #include <string> namespace F_ile {// 读取文件&#xff0c;返回文件内容bool readFilename(const std::string& filePath);bool readFilePubilcpath(const std::string& filePath);bool getNameFro…...

matlab显示sin二维图

1&#xff0c;新建脚本 2、保存脚本 3、脚本命令&#xff1a;clc 清除 脚本命令的信息 clrear all 清除全部 4工作区内容&#xff1a;变量啥的 x0:0.001:2*pi%% 开始 精度 中值 ysin(x) y1cos(x) figure%%产生一个屏幕 plot(x,y)%%打印坐标 title(ysin(x))%%标题 xlabel(…...

验证 kubelet 服务已经停止并且不再生成错误日志

要验证 kubelet 服务已经停止并且不再生成错误日志&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 检查 kubelet 服务状态 首先&#xff0c;确认 kubelet 服务已经停止。 Bash 深色版本 sudo systemctl status kubelet 你应该看到类似以下的输出&#xff0c;表示服…...

【Linux】进程控制-----进程替换

目录 一、为什么要进行进程替换&#xff1a; 二、进程替换的原理&#xff1a; 三、exec家族&#xff1a; 1、execl&#xff1a; 2、execlp&#xff1a; 3、execv&#xff1a; 4、execvp&#xff1a; 5、execle和execve ​编辑 putenv&#xff1a; 一、为什么要进行进程…...

安装SQL Server 2022提示需要Microsoft .NET Framework 4.7.2 或更高版本

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

使用ECharts创建带百分比标注的环形图

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

学习threejs,设置envMap环境贴图创建反光效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.CubeTextureLoader 立…...

go语言里的mkdir mkdirall有什么区别?

在Go语言中&#xff0c;os.Mkdir 和 os.MkdirAll 都是用来创建目录的函数&#xff0c;但它们之间存在一些关键的区别。 ### os.Mkdir - **功能**&#xff1a;os.Mkdir 用于创建一个单一的目录。如果该目录已经存在&#xff0c;则会返回一个错误。 - **参数**&#xff1a; - na…...

使用Python OpenCV实现图像形状检测

目录 一、环境准备 二、读取和预处理图像 读取图像 灰度化 滤波去噪 三、边缘检测 四、查找轮廓 五、绘制轮廓 六、形状分类 七、显示结果 八、完整代码示例 九、总结 图像形状检测是计算机视觉领域中的一项关键技术,广泛应用于工业自动化、机器人视觉、医学图像处…...

继上一篇,设置弹框次数以及自适应图片弹框,部分机型(vivo)老手机不显示的问题

上一篇写的本来测试好多型号都无事, 今天下午公司的战斗机vivo横空冒出… 晕 弹框直接显示都出不来了,现在还有用这种老的机型的,但是没办法咯~ 前端遇到这种兼容性的问题就要勇于解决 主要解决了这几点: // 添加图片加载事件 <imgv-if"imageUrl":src"image…...

基于RISC-V 的代理内核实验(使用ub虚拟机安装基本环境)

1.安装支撑软件 第一步&#xff0c;安装依赖库 RISC-V交叉编译器的执行仍然需要一些本地支撑软件包&#xff0c;可使用以下命令安装&#xff1a; $ sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bis…...

【MMKV】HarmonyOS中的优秀轻量化存储方式

MMKV 引言 在移动应用开发的世界里&#xff0c;数据存储和管理是至关重要的一环。随着技术的不断进步&#xff0c;开发者们对于高性能、轻量级、易用的数据存储解决方案的需求日益增长。MMKV&#xff08;Memory Mapped Key-Value&#xff09;正是这样一个开源的高性能key-val…...

docker安装hadoop环境

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

开源多媒体处理工具ffmpeg是什么?如何安装?使用ffmpeg将M3U8格式转换为MP4

目录 一、FFmpeg是什么二、安装FFmpeg&#xff08;windows&#xff09;三、将M3U8格式转换为MP4格式 一、FFmpeg是什么 FFmpeg是一款非常强大的开源多媒体处理工具&#xff0c;它几乎可以处理所有类型的视频、音频、字幕以及相关的元数据。 FFmpeg的主要用途包括但不限于&…...

算法刷题Day5: BM52 数组中只出现一次的两个数字

描述&#xff1a; 一个整型数组里除了两个数字只出现一次&#xff0c;其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。 要求&#xff1a;空间复杂度 O(1)&#xff0c;时间复杂度O(n)。 题目传送门 is here 思路&#xff1a; 方法一&#xff1a;最简单的思路就…...

55 基于单片机的方波频率可调

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 采用STC89C52单片机最小系统&#xff0c;设计DAC0832、放大器、与示波器显示方波&#xff0c;四位数码管显示频率&#xff0c;两个按键可调。 二、硬件资源 基于KEIL5编写C代码&#xff0c;PROT…...

23.useUnload

在 Web 应用开发中,处理页面卸载(unload)事件是一个重要但常常被忽视的方面。无论是提醒用户保存未保存的更改,还是执行一些清理操作,都需要在用户即将离开页面时进行处理。useUnload 钩子提供了一种简洁的方式来在 React 组件中处理 beforeunload 事件,使得在用户试图关…...

linux环境搭建

1、**连接外网** ssh在192.168.4.x上运行sudo ip link set ens160 down ssh切换到192.168.3.x(外网ip)&#xff0c;运行sudo ip route add default via 192.168.2.1 dev ens192 onlink //连接外网 使用完外网后 ssh在192.168.3.x上运行sudo ip link set ens160 up ssh在1…...

《C++与生物医学的智能融合:医疗变革新引擎》

在当今科技飞速发展的时代&#xff0c;人工智能正以前所未有的深度和广度渗透到各个领域&#xff0c;为传统行业带来革新与突破。其中&#xff0c;将 C与生物学、医学等领域知识相结合&#xff0c;开发用于处理生物医学数据、辅助疾病诊断和治疗的人工智能应用&#xff0c;成为…...

Matlab 绘制雷达图像完全案例和官方教程(亲测)

首先上官方教程链接 polarplothttps://ww2.mathworks.cn/help/matlab/ref/polarplot.html 上实例 % 定义角度向量和径向向量 theta linspace(0, 2*pi, 5); r1 [1, 2, 1.5, 2.5, 1]; r2 [2, 1, 2.5, 1.5, 2];% 绘制两个雷达图 polarplot(theta, r1, r-, LineWidth, 2); hold …...

Lua的环境与热更

一、global_State,lua_State与G表 Lua支持多线程环境&#xff0c;使用 lua_State 结构来表示一个独立的 Lua 线程&#xff08;或协程&#xff09;。每个线程都需要一个独立的全局环境。而lua_State 中的l_G指针&#xff0c;指向一个global_State结构&#xff0c;这个就是我们常…...

HTML CSS JS基础考试题与答案

一、选择题&#xff08;2分/题&#xff09; 1&#xff0e;下面标签中&#xff0c;用来显示段落的标签是&#xff08; d &#xff09;。 A、<h1> B、<br /> C、<img /> D、<p> 2. 网页中的图片文件位于html文件的下一级文件夹img中&#xff0c;…...

若依解析(一)登录认证流程

JWTSpringSecurity 6.X 实现登录 JWT token只包含uuid ,token 解析uuid&#xff0c;然后某个常量加UUID 从Redis缓存查询用户信息 流程图如下 感谢若依&#xff0c;感谢开源&#xff0c;能有这么好系统供我学习。 设计数据库&#xff0c;部门表&#xff0c;用户表&#xff0c…...

Redis设计与实现第17章 -- 集群 总结1(节点 槽指派)

集群通过分片sharding来进行数据共享&#xff0c;并提供复制和故障转移功能。 17.1 节点 一个Redis集群通常由多个节点node组成&#xff0c;刚开始每个节点都是相互独立的&#xff0c;必须将各个独立的节点连接起来&#xff0c;才能构成一个包含多个节点的集群。通过CLUSTER …...

汽车控制软件下载移动管家手机控车一键启动app

移动管家手机控制汽车系统是一款实现车辆远程智能控制的应用程序‌。通过下载并安装特定的APP&#xff0c;用户可以轻松实现以下功能&#xff1a;‌远程启动与熄火‌&#xff1a;无论身处何地&#xff0c;只要有网络&#xff0c;即可远程启动或熄火车辆&#xff0c;提前预冷或预…...

推荐几个可以免费下载网站模板的资源站

推荐几个可以免费下载网站模板的资源站&#xff0c;上面有免费的wordpress模板和帝国CMS模板可以下载。 模板帝 Mobandi.com 模板帝是一个提供丰富网站模板资源的平台&#xff0c;旨在帮助用户快速构建和美化自己的网站。无论是个人博客、企业官网还是电子商务平台&#xff…...

H3C OSPF实验

实验拓扑 实验需求 按照图示配置 IP 地址按照图示分区域配置 OSPF &#xff0c;实现全网互通为了路由结构稳定&#xff0c;要求路由器使用环回口作为 Router-id&#xff0c;ABR 的环回口宣告进骨干区域 实验解法 一、配置IP地址 [R1]int l0 [R1-LoopBack0]ip add 1.1.1.1 32 […...

公众号自己做电影网站吗/常见的营销方式有哪些

2019独角兽企业重金招聘Python工程师标准>>> DefaultBeanDefinitionDocumentReader.processBeanDefinition() 完成 Bean 标签解析的核心工作&#xff1a; 解析工作分为三步&#xff1a; 解析默认标签&#xff1b;解析默认标签后下得自定义标签&#xff1b;注册解析后…...

如何建立免费的网站/网站建设步骤

情景&#xff1a;在开发过程中&#xff0c;需要将两个对象实体的值进行对比 处理&#xff1a;使用BeanUtils.describe将两个对象转换成Map进行遍历对比数据 问题&#xff1a;发现BeanUtils.describe转换后的Map的泛型是<String,String>此时获取到的字段如果是数组类型的…...

高要市建设局网站/博客网站登录入口

介绍 在短视频火爆的今天&#xff0c;视频剪辑软件因能对素材进行多样化的加工而广受欢迎。在本文&#xff0c;我对VUE视频相机、爱剪辑、Quik三款手机应用软件做了简单测评。 首先简单罗列一下这三款软件的一些信息&#xff0c;并简单介绍一下相关情况 表1.相关信息 名称 VU…...

com域名的网站/网店代运营靠谱吗

const是一个C语言的关键字&#xff0c;它限定一个变量不允许被改变。使用const在一定程度上可以提高程序的安全性和可靠性&#xff0c;另外&#xff0c;在观看别人代码的时候&#xff0c;清晰理解const所起的作用&#xff0c;对理解对方的程序也有一些帮助。另外CONST在其他编程…...

苏州市建设局老网站/网易疫情实时最新数据

后续待续&#xff01;...

做独立网站可以支付下单/千锋教育的官网

在数组末尾添加元素 这里以在空数组中添加一个元素99为例&#xff1a; 以删除第二个元素为例 public class mian {//创建用于存储数据的数组private int[] elements;public mian(){elementsnew int[0];}//创建一个可以在数组末尾添加元素的方法 public void add(int element) /…...