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

南京网站建设与网络营销的关系/mac923水蜜桃923色号

南京网站建设与网络营销的关系,mac923水蜜桃923色号,品牌网站建设公司推荐,中山市建设局安全监督站网站了解MVVM 什么是MVVM:一种设计模式 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人…

了解MVVM

  1. 什么是MVVM:一种设计模式

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

  1. 为什么需要MVVM?解决什么问题?

降低耦合、独立开发、逻辑重用(Xamarin MAUI多平台应用)、可测试

响应式布局

控件交互到MVVM模式的转变

控件交互:基于控件的功能开发

MVVM模式

代码/项目结构

Models:

Views:

ViewModels:

需求:计算器,输入:A B 输出:通过按钮 A+B 的结果

控件交互写法:

XAML代码:

<StackPanel><TextBox Text="" Name="tb_1"/><TextBlock Text="+" /><TextBox Text="" Name="tb_2"/><TextBlock Text="=" /><TextBox Text="" Name="tb_3"/><Button Content="计算" Click="Button_Click"/>
</StackPanel>

C#代码:

private void Button_Click(object sender, RoutedEventArgs e)
{// 控制逻辑double.TryParse(this.tb_1.Text, out double value_1);double.TryParse(this.tb_2.Text, out double value_2);this.tb_3.Text = (value_1 + value_2) + "";
}

效果:

MVVM模式开发:

XAML代码:View代码

在XAML或内部cs文件中,绑定对应的ViewModel

<Window x:Class="XH.MvvmLesson.Mvvm.MvvmWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:XH.MvvmLesson.Mvvm"mc:Ignorable="d"Title="MvvmWindow" Height="450" Width="800"><!--绑定ViewModel--><Window.DataContext><local:LogicClass /></Window.DataContext><StackPanel><TextBox Text="{Binding _model.Value1}" /><TextBlock Text="+" /><TextBox Text="{Binding _model.Value2}" /><TextBlock Text="=" /><TextBox Text="{Binding _model.Value3}" /><Button Content="计算" Command="{Binding BtnCommand}" CommandParameter="BtnParameter"/><Button Content="检查状态" Command="{Binding BtnCheckCommand}" /></StackPanel>
</Window>

ViewModel代码:

创建实例,绑定View

创建Command对象,绑定事件

namespace XH.MvvmLesson.Mvvm
{public class LogicClass{public DataModel _model { get; set; } = new DataModel();public Command BtnCommand { get; set; }public Command BtnCheckCommand { get; set; }public LogicClass(){BtnCommand = new Command(DoLogic, CanDoLogic);BtnCheckCommand = new Command(DoCheck);_model.PropertyChanged += _model_PropertyChanged;}// 属性变化事件private void _model_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e){BtnCommand.RaiseCanExecuteChanged();}// 检查按钮private void DoCheck(object obj){BtnCommand.RaiseCanExecuteChanged();}// 控制逻辑private void DoLogic(object obj){_model.Value3 = _model.Value1 + _model.Value2;}// 是否可以启用按钮private bool CanDoLogic(object obj){return _model.Value1 > 0 && _model.Value2 > 0;}}
}

Model代码:

INotifyPropertyChanged:使用页面通知属性,在属性set的时候,调用

using System.ComponentModel;namespace XH.MvvmLesson.Mvvm
{// 针对功能所提供的数据模型public class DataModel : INotifyPropertyChanged{private double _value1;public double Value1{get { return _value1; }set{_value1 = value;PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("Value1"));}}private double _value2;public double Value2{get { return _value2; }set{_value2 = value;PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("Value2"));}}private double _value3;public double Value3{get { return _value3; }set{_value3 = value;// 在系统内广而告之 告诉页面上哪个对象关注了这个实例里的这个属性的,赶紧更新结果PropertyChanged?.Invoke(this,new PropertyChangedEventArgs("Value3"));}}// 做信息发布的对象 需要执行这一下这个对象public event PropertyChangedEventHandler? PropertyChanged;}
}

事件Command代码:ICommand

需要继承ICommand接口

CanExecute:判断是否需要启用这个事件,等同于IsEnabled

在界面加载的时候调用一次,在每次执行之前再调用一次

Execute:执行事件方法

parameter:界面的CommandParameter属性来传值

using System.Windows.Input;namespace XH.MvvmLesson.Mvvm
{public class Command : ICommand{// 判断绑定的当前命令实例的对象 是否可用// 比如按钮是否可以执行下面的逻辑public event EventHandler? CanExecuteChanged;// 外部调用public void RaiseCanExecuteChanged(){// 通知方法使用是否可以触发 通知状态检查CanExecuteChanged?.Invoke(this, EventArgs.Empty);}// 作用,触发一个时机 切换调用方的状态是否可用public bool CanExecute(object? parameter){return _canExecute?.Invoke(parameter) != false;}// 参数 返回值private Func<object?, bool> _canExecute;// 绑定了当前命令实例的对象的执行逻辑// 等同于 Button的Click 事件// parameter:界面的CommandParameter属性来传值public void Execute(object? parameter){// 委托 DoExeute?.Invoke(parameter);}public Action<object> DoExeute { get; set; }public Command(Action<object> action, Func<object?, bool> func = null){DoExeute = action;_canExecute = func;}}
}

MVVM绑定模式下的信息交互

数据类型:INotifyPropertyChanged接口
class Class1 : INotifyPropertyChanged
{public event PropertyChangedEventHandler? PropertyChanged;
}
行为动作:ICommand接口
class Class1 : ICommand
{// 调用这个方式的时候 又调用 CanExecute 方法public event EventHandler? CanExecuteChanged;// 外部调用public void RaiseCanExecuteChanged(){// 通知方法使用是否可以触发 通知状态检查CanExecuteChanged?.Invoke(this, EventArgs.Empty);}// 作用,触发一个时机 切换调用方的状态是否可用public bool CanExecute(object? parameter){throw new NotImplementedException();}// 绑定了当前命令实例的对象的执行逻辑// 等同于 Button的Click 事件// parameter:界面的CommandParameter属性来传值public void Execute(object? parameter){throw new NotImplementedException();}
}

扩展:修改数据格式,把每次的算法记录成一个表格,并且添加删除按钮可以删除

主要是修改表格的数据模板,并且每个数据增加个按钮 进行移除,并且传入当前集合,集合通知属性

部分XAML代码:

 <Grid><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions><StackPanel><TextBox Text="{Binding _model.Value1,UpdateSourceTrigger=PropertyChanged}" /><TextBlock Text="+" /><TextBox Text="{Binding _model.Value2,UpdateSourceTrigger=PropertyChanged}" /><TextBlock Text="=" /><TextBox Text="{Binding _model.Value3}" /><Button Content="计算" Command="{Binding BtnCommand}" CommandParameter="BtnParameter"/><Button Content="检查状态" Command="{Binding BtnCheckCommand}" /></StackPanel><ListBox Grid.Column="1" ItemsSource="{Binding ResultList}" Name="lb"><ListBox.ItemTemplate><DataTemplate><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding}" /><!--<TextBlock Text="{Binding State}" />--><!--只写个Binding 是绑定当前数据源--><Button Content="删除" CommandParameter="{Binding}"Command="{Binding DataContext.BtnDelCommand,RelativeSource={RelativeSource AncestorType=Window}}" /></StackPanel></DataTemplate></ListBox.ItemTemplate></ListBox></Grid>

只写个Binding 是绑定当前数据源

部分C#代码:

ObservableCollection:集合通知属性

// ObservableCollection:集合通知属性,代替List 可以通知界面修改数据
//public ObservableCollection<ResultModel> ResultList { get; set; } = new ObservableCollection<ResultModel>();
public ObservableCollection<string> ResultList { get; set; } = new ObservableCollection<string>();public Command BtnDelCommand { get; set; }public MainViewModel()
{BtnDelCommand = new Command(DoDel);
}// 删除按钮
private void DoDel(object obj)
{ResultList.Remove((string)obj);
}// 控制逻辑
private void DoLogic(object obj)
{_model.Value3 = _model.Value1 + _model.Value2;// 如果希望通知子项的变化(这个集合中的子项的增减)// 需要实现INotifyCollectionChanged接口,进行子项变化通知// 框架提供了通知集合对象ObservableCollection//ResultList.Add(new ResultModel//{//    Msg = $"第{ResultList.Count + 1}次{_model.Value1} + {_model.Value2} = {_model.Value3}",//});ResultList.Add($"第{ResultList.Count + 1}次{_model.Value1} + {_model.Value2} = {_model.Value3}");
}

显示效果:

MVVM绑定扩展

无法绑定的对象属性

通过附加属性进行扩展

案例:绑定ScottPlot,实现图标动态刷新

View:

<Window x:Class="XH.MvvmPattern.Views.ScottPlotWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:XH.MvvmPattern.Views"xmlns:vm="clr-namespace:XH.MvvmPattern.ViewModels"xmlns:b="clr-namespace:XH.MvvmPattern.Base"mc:Ignorable="d"Title="ScottPlotWindow" Height="450" Width="800"><Window.DataContext><vm:ScottPlotViewModel /></Window.DataContext><Grid><WpfPlot Name="wpf_plot" b:ScottPlotExtension.Values="{Binding Datas}"/></Grid>
</Window>

Model:由于数据很少,数据写在ViewModel 里面

ViewModel:

using ScottPlot;
using System.Collections.ObjectModel;
using System.Windows;namespace XH.MvvmPattern.ViewModels
{public class ScottPlotViewModel{public ObservableCollection<double> Datas { get; set; } public ScottPlotViewModel(){Datas = new ObservableCollection<double>(DataGen.RandomWalk(new Random(), 10));// 实时监控 持续获取数据 Task.Run(async () =>{while (true){await Task.Delay(1000);Application.Current.Dispatcher.BeginInvoke(() =>{Datas.Add(new Random().NextDouble());Datas.RemoveAt(0);});}});}}
}

附加属性:

using ScottPlot;
using System.Collections.ObjectModel;
using System.Windows;namespace XH.MvvmPattern.Base
{public class ScottPlotExtension{// 依赖附加属性// 这个属性里面可以知道绑定的属性 以及被附加的对象public static ObservableCollection<double> GetValues(DependencyObject obj){return (ObservableCollection<double>)obj.GetValue(ValuesProperty);}public static void SetValues(DependencyObject obj, int value){obj.SetValue(ValuesProperty, value);}// Using a DependencyProperty as the backing store for Values.  This enables animation, styling, binding, etc...public static readonly DependencyProperty ValuesProperty =DependencyProperty.RegisterAttached("Values",typeof(ObservableCollection<double>),typeof(ScottPlotExtension),new PropertyMetadata(null, new PropertyChangedCallback(OnValuesChanged)));// 当给Value赋值的时候 才会触发private static void OnValuesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var plt = d as WpfPlot;var newPlt = plt.Plot;var new_list = (ObservableCollection<double>)e.NewValue;var signal = newPlt.AddSignal(new_list.ToArray());// 对集合实例进行子项增减的时候 进行回调new_list.CollectionChanged += (s, e) =>{// 由于数组长度不能变 所以是ViewModel中,长度变为10的事件中,再触发,加个判断if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove){signal.Update(new_list.ToArray());plt.Refresh();}};plt.Refresh();}}
}

注意:附加属性中的属性改变事件,只有在Value赋值的时候才会触发,但是List中间值改变不会触发,

所以需要再此事件中写集合的值改变事件:CollectionChanged

无法绑定的动作事件

单击鼠标左键 :LeftClick

双击鼠标左键:LeftDoubleClick

单击鼠标中键 :MiddleClick

双击鼠标中键:MiddleDoubleClick

单击鼠标右键:RightClick

双击鼠标右键:RightDoubleClick

不执行任何操作:None

旋转鼠标滚轮:WheelClick

键盘和鼠标常用方法:InputBindings

<Border.InputBindings><MouseBinding Command="{Binding BtnCommand}" MouseAction="LeftClick" /><KeyBinding Command="{Binding BtnCommand}" Key="Enter" />
</Border.InputBindings>
事件转命令

Behavior

第一步:下载NuGet 包

第二步:引入命名空间

xmlns:b="http://schemas.microsoft.com/xaml/behaviors"

第三步:使用:

EventName:绑定的事件名称

Command:绑定

 <Border Height="40" Background="Orange"><b:Interaction.Triggers><b:EventTrigger EventName="MouseLeftButtonDown"><b:InvokeCommandAction Command="{Binding BtnCommand}" /></b:EventTrigger></b:Interaction.Triggers></Border>

自定义实现

思路:就是写一个附加属性,然后同过一个类,传入事件名称和事件Command,然后同过反射反射到对应的控件事件上,进行调用即可。

C#代码:

using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;namespace XH.MvvmPattern.Base
{// 任意事件绑定public class EventExtension{// 解决:页面至少需要传递两个信息:事件名陈、命令,可以将这两个信息进行类的打包 EventCommand// 通过EventCommand的实例进行相关信息的获取(事件名称、命令)// 然后进行反射事件的委托挂载,在执行命令过程public static EventCommand GetEventTarget(DependencyObject obj){return (EventCommand)obj.GetValue(EventTargetProperty);}public static void SetEventTarget(DependencyObject obj, EventCommand value){obj.SetValue(EventTargetProperty, value);}// Using a DependencyProperty as the backing store for EventTarget.  This enables animation, styling, binding, etc...public static readonly DependencyProperty EventTargetProperty =DependencyProperty.RegisterAttached("EventTarget", typeof(EventCommand), typeof(EventExtension), new PropertyMetadata(null,new PropertyChangedCallback(OnEventTargetChanged)));private static void OnEventTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){if (d is UIElement control){var eventCommand = e.NewValue as EventCommand;if (eventCommand == null) return;string eventName = eventCommand.EventName;// 获取控件类型	Type controlType = control.GetType();bool isEvent = IsEvent(eventName, controlType);if (isEvent){EventInfo eventInfo = controlType.GetEvent(eventName);if (eventInfo != null){// 动态挂载事件MethodInfo methodInfo = typeof(EventExtension).GetMethod("OnEventRaised", BindingFlags.NonPublic | BindingFlags.Static);Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, methodInfo);eventInfo.AddEventHandler(control, handler);control.SetValue(EventTargetProperty, eventCommand);}}}}// 挂载事件 事件转命令 private static void OnEventRaised(object sender, EventArgs e){DependencyObject control = sender as DependencyObject;if (control == null) return;EventCommand eventCommand = GetEventTarget(control);if (eventCommand == null) return;ICommand command = eventCommand.Command;// 如果Command 获取为null,有可能是因为EventCommand对象的继承有问题,需要继承Animatableif (command != null && command.CanExecute(eventCommand.CommandParameter)){command.Execute(eventCommand.CommandParameter);}}// 判断是不是事件private static bool IsEvent(string eventName, Type controlType){// 获取控件类型的所有事件EventInfo[] events = controlType.GetEvents();// 检查是否存在与给定名称匹配的事件foreach (EventInfo eventInfo in events){if (eventInfo.Name.Equals(eventName, StringComparison.OrdinalIgnoreCase)){return true;}}return false;}}public class EventCommand : Animatable{public string EventName { get; set; }// 事件绑定public ICommand Command{get { return (ICommand)GetValue(CommandProperty); }set { SetValue(CommandProperty, value); }}// Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...public static readonly DependencyProperty CommandProperty =DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommand), new PropertyMetadata(null));// 参数public object CommandParameter{get { return (object)GetValue(CommandParameterProperty); }set { SetValue(CommandParameterProperty, value); }}// Using a DependencyProperty as the backing store for CommandParameter.  This enables animation, styling, binding, etc...public static readonly DependencyProperty CommandParameterProperty =DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommand), new PropertyMetadata(0));protected override Freezable CreateInstanceCore(){return (Freezable)Activator.CreateInstance(GetType());}}
}

注意:如果在挂载的时候,发现Command是null,或者绑定的时候目标源在FrameworkElement 或 FrameworkContentElement上的时候,需要继承Animatable类,也不是继承接口:ICommand

主要是仿照微软提供的behaviors进行编写,这个方法也是继承Animatable才能实现Command 传递

ContextMenu绑定事件处理

XAML代码:原因:ContextMenu在窗体之上,不在窗体之内,所以找不到窗体的数据源,需要明确指定数据源

<Window.Resources><vm:MainViewModel x:Key="mvm" />
</Window.Resources>
<Window.DataContext><!--<vm:MainViewModel />--><StaticResource ResourceKey="mvm" />
</Window.DataContext>
………………
<StackPanel.ContextMenu><ContextMenu><!--第一种方式 Source={x:Reference Name=lb}--><!--<MenuItem Header="删除" CommandParameter="{Binding}" Command="{Binding DataContext.BtnDelCommand,Source={x:Reference Name=lb}}"/>--><!--第二种方式 把数据源当做资源调用 Source 即可--><!--原因:ContextMenu在窗体之上,不在窗体之内,所以找不到窗体的数据源,需要明确指定数据源注意:命令的数据源的指定--><MenuItem Header="删除" CommandParameter="{Binding}" Command="{Binding BtnDelCommand,Source={StaticResource mvm}}"/></ContextMenu>
</StackPanel.ContextMenu>
自定义控件的事件绑定:

C#代码:直接在代码中写依赖绑定属性即可,在有需要的地方调用Execute

public partial class DateTimePicker :UserControl, INotifyPropertyChanged{// 命令属性public ICommand SelectedCommand{get { return (ICommand)GetValue(SelectedCommandProperty); }set { SetValue(SelectedCommandProperty, value); }}// Using a DependencyProperty as the backing store for SelectedCommand.  This enables animation, styling, binding, etc...public static readonly DependencyProperty SelectedCommandProperty =DependencyProperty.Register("SelectedCommand", typeof(ICommand), typeof(DateTimePicker), new PropertyMetadata(null));public object SelectedCommandParameter{get { return (object)GetValue(SelectedCommandParameterProperty); }set { SetValue(SelectedCommandParameterProperty, value); }}// Using a DependencyProperty as the backing store for SelectedCommandParameter.  This enables animation, styling, binding, etc...public static readonly DependencyProperty SelectedCommandParameterProperty =DependencyProperty.Register("SelectedCommandParameter", typeof(object), typeof(DateTimePicker), new PropertyMetadata(null));…………………………// 在需要调用的地方 调用此事件即可private void Button_Click(object sender, RoutedEventArgs e){// 命令的执行SelectedCommand?.Execute(SelectedCommandParameter);}}

XAML代码:

<c:DateTimePicker VerticalAlignment="Top" HorizontalAlignment="Center" SelectedCommand="{Binding SeletedCommand}" SelectedCommandParameter="{Binding}"/>

MVVM分层逻辑相关问题

VM逻辑中弹窗操作

需求:应用中逻辑处理过程中需要打开一个子窗口,作为Dialog窗口进行信息显示

利用一个第三方对象WindowProvider,允许View层进行窗口对象的注册 ,允许ViewModel层进行窗口对象的调用请求

过程中涉及两方的数据传递

第三方对象WindowProvider:

using System.Windows;namespace XH.Mvvm.Base
{public class WindowProvider{static Dictionary<string, WindowInfo> types = new Dictionary<string, WindowInfo>();// 收购窗口// 允许自定义名字 key,不自定义就是窗体名public static void Register<T>(string key = "",Window owner = null){if (string.IsNullOrEmpty(key))key = typeof(T).Name;if (!types.ContainsKey(key))types.Add(key, new WindowInfo { WinType = typeof(T),OwnerType = owner });}// 出售public static bool ShowDialog(string key,object dataContext){Type type = null;if (types.ContainsKey(key)){type = types[key].WinType;}if (type != null){// 同过反射创建新的对象Window win = (Window)Activator.CreateInstance(type);// 设置窗口所有者 当任务栏打开大窗口的时候,小窗口带出win.Owner = types[key].OwnerType;win.DataContext = dataContext;bool state = (bool)win.ShowDialog();return state;}elsethrow new Exception("没有找到对应的弹窗对象");}}class WindowInfo{public Type WinType { get; set; }public Window OwnerType { get; set; }}
}

View地方调用:

 public MainWindow(){InitializeComponent();WindowProvider.Register<SubWindow>(owner: this);}

ViewModel地方调用:

public MainViewModel()
{BtnCommand = new Command(obj =>{// Btn的执行逻辑 打开子窗口// 因为不能View-->ViewModel-->View 来回调用,此方法不可取//new SubWindow().ShowDialog();SubViewModel subViewModel = new SubViewModel();subViewModel.Value = Value;if (WindowProvider.ShowDialog("SubWindow", subViewModel)){// 获取到子窗口返回的Value属性this.Value = subViewModel.Value;}else { }// 如何从子窗口返回到主窗口:SUbViewModel --> MainViewModel// 1、子窗口打开后,编辑完成,保存按钮、取消按钮// 2、最终数据不在子窗口处理,返回到主VM再处,保存、回退});
}

显示:能够顺利显示和传值

逻辑:

  1. 收购
    1. 定义一个字典,然后记录需要弹出的窗口信息(窗口名称,父窗口)和key,用key和信息进行绑定,然后调用的时候用key调用窗口信息。
  1. 出售
    1. 如果存在这个key,然后同过反射把字典中的窗口信息反射出来给Window,然后进行设置Window的属性,并且弹窗,同过DialogResult属性来判断是否需要写入信息。
    2. 出售(调用)的时候,把需要传入的Model通过参数传进来。
  1. 调用
    1. 通过出售的时候返回的bool信息,来判断是否需要保存下来返回的值
任意对象间逻辑调用

委托对象的管理,被动方进行委托方法的注册,主动方进行委托方法的请求调用

核心 :委托对象的使用

ActionManager类:


namespace XH.Mvvm.Base
{public class ActionManager{static Dictionary<string, Delegate> types = new Dictionary<string, Delegate>();public static void Register<T>(Action<T> action, string key){if (!types.ContainsKey(key))types.Add(key, action);}  public static void Register<T>(Func<T> action, string key){if (!types.ContainsKey(key))types.Add(key, action);}public static void Invoke<T>(string key, T arg){if (types.ContainsKey(key))((Action<T>)types[key]).Invoke(arg);}public static T InvokeWithResult<T>(string key){if (types.ContainsKey(key))return (types[key] as Func<T>).Invoke();return default(T);}}
}

A窗体进行订阅:

public MainViewModel()
{// 订阅的过程,ActionManager.Register<object>(new Action<object>(DoAction), "AAA");ActionManager.Register<object>(new Func<object>(DoFunc), "BBB");}private object DoFunc(){return this.Value;}private void DoAction(object obj){}

B窗体进行调用:

public SubViewModel()
{SubmitCommand = new Command(obj =>{// 发布的动作// B窗口传递给A窗口值ActionManager.Invoke<object>("AAA",Value);// A窗口传递给B窗口值var v = ActionManager.InvokeWithResult<object>("BBB");});
}

逻辑:

  1. 注册
    1. 注册一个委托key字典,然后同过key和方法进行注册,发布的时候同过key进行搜索委托事件执行。
    2. 可以注册Action 和 Func 方法,Action是调用的时候需要参数,Func调用的时候是返回参数
    3. 注册Func方法的时候,需要返回一个Object类型的参数,给调用方
  1. 调用/发布
    1. 同过Key进行调用,但是需要传入参数,如果是Action的话,需要传给注册方一个object参数,在注册方的方法中。
    2. 如果是Func的话,返回一个参数,是注册方返回给掉用方的参数

注意:Func 和 Action 的区别

Func
  • 定义:Func是一个泛型委托,用于表示可以带有参数并且返回值的方法。Func委托可以有多个输入参数,但只能有一个返回值。
  • 用途:通常用于需要执行一个操作并返回结果的情况。例如,你可能需要一个方法来检查某个字符串是否符合特定的格式,并返回一个布尔值表示检查结果。
  • 语法示例Func<T, TResult> 表示一个接受一个类型为T的参数并返回一个类型为TResult的结果的委托。Func<int, string, bool> 表示一个接受一个int类型和一个string类型参数,并返回一个bool类型结果的委托。
Action
  • 定义:Action是另一个泛型委托,但它不返回任何值(即返回类型为void)。Action委托可以有多个输入参数,但没有返回值。
  • 用途:适用于那些执行操作但不需要返回结果的情况。例如,你可能需要一个方法来打印日志信息,或者更新某个对象的状态,这些操作都不需要返回值。
  • 语法示例Action<T> 表示一个接受一个类型为T的参数但不返回任何结果的委托。Action<int, string> 表示一个接受一个int类型和一个string类型参数,但不返回任何结果的委托。
Func和Action的主要区别归纳

特性

Func

Action

返回值

有返回值

无返回值(void)

参数数量

可以有0到多个参数

可以有0到多个参数

用途

执行操作并返回结果

执行操作但不返回结果

语法示例

Func<T, TResult>

Action<T>

总结

Func和Action是编程中用于表示不同类型方法的泛型委托。Func用于表示需要返回值的方法,而Action用于表示不需要返回值的方法。它们的使用取决于你的具体需求,即在执行某个操作时是否需要返回结果。通过合理使用Func和Action,可以使代码更加清晰、灵活和易于维护。

相关文章:

WPF Mvvm

了解MVVM 什么是MVVM&#xff1a;一种设计模式 设计模式&#xff08;Design pattern&#xff09;代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人…...

pnpm【实用教程】2024最新版

pnpm 简介 pnpm 全称 performant npm&#xff0c;即高性能的 npm&#xff0c;由 npm/yarn 衍生而来&#xff0c;解决了 npm/yarn 内部潜在的 bug&#xff0c;极大的优化了性能&#xff0c;扩展了使用场景&#xff0c;被誉为 最先进的包管理工具 安装 pnpm npm i -g pnpm使用 pn…...

C#的前沿技术有哪些?

C#作为.NET平台的核心语言&#xff0c;其前沿技术主要围绕.NET生态系统的扩展和更新展开。了解C#的前沿技术对于开发者来说至关重要&#xff0c;因为它们代表了该语言和平台的最新发展方向和趋势。目前&#xff0c;C#的前沿技术主要集中在以下几个方面&#xff1a; 1. NET 6: …...

Vue2移动端(H5项目)项目基于vant封装图片上传组件(支持批量上传、单个上传、回显、删除、预览、最大上传数等功能)---解决批量上传问题

一、最终效果 二、参数配置 1、代码示例&#xff1a; <t-uploadfileList"fileList":showFileList"showFileList"showFile"showFile":showFileUrl"showFileUrl"/>2、配置参数&#xff08;TUpload Attributes&#xff09;继承va…...

ELK整合实战,filebeat和logstash采集SpringBoot项目日志发送至ES

文章目录 ELK整合实战使用FileBeats将日志发送到Logstash配置Logstash接收FileBeat收集的数据并打印Logstash输出数据到Elasticsearch利用Logstash过滤器解析日志Grok插件Grok语法用法 输出到Elasticsearch指定索引 前文&#xff1a;FileBeats详解 前文&#xff1a;logstash详解…...

网络编程:OSI协议,TCP/IP协议,IP地址,UDP编程

目录 国际网络通信协议标准&#xff1a; 1.OSI协议&#xff1a; 2.TCP/IP协议模型&#xff1a; 应用层 &#xff1a; 传输层&#xff1a; 网络层&#xff1a; IPV4协议 IP地址 IP地址的划分&#xff1a; 公有地址 私有地址 MA…...

QtExa001自动包装流水线的框架设计vs2019QT

QtExa001自动包装流水线的框架设计 工程代码&#xff1a; https://download.csdn.net/download/txwtech/89636815https://download.csdn.net/download/txwtech/89636815 主界面&#xff1a; 设置&#xff1a;进行参数配置&#xff0c;保存ini文件 调试&#xff1a;tcp/ip&…...

SpringBoot拦截器的使用介绍

SpringBoot拦截器的使用介绍 本篇文章主要讲的是 SpringBoot 拦截器的使用介绍。 1、定义拦截器 拦截器&#xff1a;所谓拦截器&#xff0c;就是能够在进行某个操作之前拦截请求&#xff0c;如果请求符合条件就允许在往下执行。 定义拦截器的几种方式。 1.1 实现HandleInt…...

Spring Boot应用中的资源分离与高效打包实践

在电商网站项目中&#xff0c;前端资源通常包括HTML、CSS、JavaScript、图片、字体等静态文件&#xff0c;以及Thymeleaf或Freemarker等模板引擎渲染的页面。将这些资源从Spring Boot主应用中分离出来&#xff0c;不仅有利于前后端团队的并行开发&#xff0c;还能提高应用的加载…...

分析 avformat_open_input 数据读取过程

------------------------------------------------------------ author: hjjdebug date: 2024年 08月 13日 星期二 17:31:43 CST descriptor: 分析 avformat_open_input 数据读取过程 ------------------------------------------------------------ avformat_open_input 中读…...

Apache HOP (Hop Orchestration Platform) VS Data Integration (通常被称为 Kettle)

Apache HOP (Hop Orchestration Platform) 和 Data Integration (通常被称为 Kettle) 都是强大的 ETL (Extract, Transform, Load) 工具&#xff0c; 它们都由 Hitachi Vantara 开发和支持。尽管它们有着相似的目标&#xff0c;即帮助用户进行数据集成任务&#xff0c;但它们在…...

如何判断一个dll/exe是32位还是64位

通过记事本判断&#xff08;可判断C或者C#&#xff09; 64位、将dll用记事本打开&#xff0c;可以看到一堆乱码&#xff0c;但是找到乱码行的第一个PE&#xff0c;如果后面是d?则为64位 32位、将dll用记事本打开&#xff0c;可以看到一堆乱码&#xff0c;但是找到乱码行的第…...

加速网页加载,提升用户体验:HTML、JS 和 Vue 项目优化全攻略

在信息爆炸的时代&#xff0c;网页加载速度成为了用户体验的重中之重。试想一下&#xff0c;如果一个页面加载超过 3 秒&#xff0c;你还有耐心等待吗&#xff1f; 为了留住用户&#xff0c;提升转化率&#xff0c;网页优化势在必行&#xff01; 本文将从 HTML、JavaScript 和…...

LVS服务器基础环境配置

环境配置 1 基础服务关闭 setenforce 0 # 临时关闭selinuxvi /etc/sysconfig/selinux # 永久关闭selinuxsystemctl disable --now firewalld # 关闭防火墙systemctl disable --now NetworkManager # 关闭网络管理器2 centos7软件仓库的配置 mount /dev/cdrom /media以防万一&…...

【Python OpenCV】使用OpenCV实现两张图片拼接

问题引入&#xff1a; 如何使用Python OpenCV实现两张图片的水平拼接和垂直拼接 代码实现&#xff1a; import cv2 import numpy as npdef image_hstack(image_path_1, image_path_2):"""两张图片左右拼接"""img1 cv2.imread(image_path_1)img…...

springboot jar -jar centos后台运行的几种方式

在CentOS系统中&#xff0c;如果你想要在后台运行一个Spring Boot应用程序&#xff0c;你可以使用nohup命令或者使用screen会话。以下是两种常用的方法&#xff1a; 1. **使用nohup命令**&#xff1a; nohup命令可以使进程在你退出SSH会话后继续运行。它还会把标准输出和标…...

【GitLab】使用 Docker 安装 GitLab:配置 SSH 端口

使用 Docker 安装 GitLab 要求修改ssh端口 GitLab 使用 SSH 通过 SSH 与 Git 交互。默认情况下,GitLab 使用端口22。 要在使用 GitLab Docker 映像时使用其他端口,您可以执行以下操作之一: 更改服务器的 SSH 端口(推荐)。 更改 GitLab Shell SSH 端口。 更改服务器的 SSH …...

【pdf文件生成】如何将盖章的文件生成PDF文件

一、提出问题 在我们的工作中&#xff0c;有时候上级让下级将盖章的文件生成PDF文件通过内部平台发送到上级邮箱&#xff0c;那如何解决呢&#xff1f;是去找一个扫描仪&#xff0c;还是用手机拍图转。用Python基实就能实现。 二、分析问题 现在网上好多的软件都是收费的&am…...

铝壳电阻在电路中的作用和影响是什么?

铝壳电阻&#xff0c;顾名思义&#xff0c;就是用铝材料制成的电阻。在电路中&#xff0c;它主要起到限流、分压、负载等作用。下面详细介绍铝壳电阻在电路中的作用和影响。 1. 限流作用&#xff1a;铝壳电阻可以限制电流的大小&#xff0c;防止电流过大而损坏电路。当电路中的…...

# Python 判断入参日期是周几

在数据分析和软件开发中&#xff0c;经常需要判断某个特定日期是星期几。Python 提供了强大的日期时间处理功能&#xff0c;可以轻松实现这一功能。本篇文章将介绍如何使用 Python 的内置库来判断给定日期是星期几&#xff0c;并提供具体实例。 1. 使用 datetime 模块 Python…...

井字棋游戏(HTML+CSS+JavaScript)

&#x1f30f;个人博客主页&#xff1a;心.c 前言&#xff1a;这两天在写植物大战僵尸&#xff0c;写不动了&#xff0c;现在和大家分享一下之前我写的一个很简单的小游戏井字棋&#xff0c;这个没有AI&#xff0c;可以两个人一起玩&#xff0c;如果大家觉得我哪里写的有一些问…...

HTML 列表和容器元素——WEB开发系列10

HTML 提供了多种方式来组织和展示内容&#xff0c;其中包括无序列表、有序列表、分区元素 ​​<div>​​ 和内联元素 ​​<span>​​、以及如何使用 ​​<div>​​​ 进行布局和表格布局。 一、HTML 列表 1. 无序列表 (​​<ul>​​) 无序列表用于展…...

Java数组的高级使用技巧与性能优化

Java数组的高级使用技巧与性能优化 大家好&#xff0c;我是微赚淘客返利系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; Java数组是程序设计中的基础数据结构&#xff0c;提供了一种存储固定大小的同类型元素的方式。本文将介绍Jav…...

python spyne报No module named ‘http.cookies‘的解决

python spyne报No module named ‘http.cookies’ python实现webservice服务端时&#xff0c;会使用spyne这个库&#xff0c;安装后&#xff0c;运行会提示No module named ‘http.cookies’。 尝试过不行的方法 pip install http.cookiespip install http.cookiejar 可行的…...

vmware虚拟机玩GPU显卡直通

安装好exsi以后&#xff0c;找到管理----硬件-----PCI设备&#xff0c;勾选想要直通的显卡&#xff0c;然后点击“切换直通” 切换以后可以看到列表中的直通列显示为活动就对了。 然后编辑虚拟机设置&#xff0c;CPU关闭硬件虚拟化&#xff08;向客户机操作系统公开硬件辅助的…...

Linux下Oracle 11g升级19c实录

1.组件信息 source /home/oracle/.bash_profile11g && sqlplus "/ as sysdba"<<EOF set line 200 col COMP_NAME for a40 select comp_name,VERSION,STATUS from dba_registry; exit; EOF COMP_NAME VERSION …...

haproxy实验-2

haproxy中的算法 静态算法&#xff1a;按照事先定义好的规则轮询公平调度&#xff0c;不关心后端服务器的当前负载、连接数和响应速度 等&#xff0c;且无法实时修改权重(只能为0和1,不支持其它值)&#xff0c;只能靠重启HAProxy生效。 static-rr&#xff1a;基于权重的轮询…...

動態PPTP代理IP是什麼?

PPTP即Point-to-Point Tunneling Protocol&#xff0c;點對點隧道協議&#xff0c;是一種常用的VPN協議&#xff0c;主要用於創建虛擬專用網路。通過將用戶的網路流量加密並通過一個中間伺服器傳輸&#xff0c;實現了對用戶IP地址的隱藏和數據的保護。而動態PPTP代理IP則是在傳…...

《全面解析 Nginx:从下载安装到高级应用与问题解决》

Nginx 一、Nginx 简介 什么是 Nginx 以及其功能 Nginx 是一款高性能的 HTTP 和反向代理的 Web 服务器&#xff0c;在处理高并发方面表现卓越&#xff0c;具备强大的能力来承受高负载&#xff0c;有相关报告指出其能够支持高达 50,000 个并发连接数。其显著特点为占用内存少、…...

python获取视频时长

今天有个需求&#xff0c;需要获取视频时长&#xff1a; 方法一&#xff1a;使用moviepy库打开视频文件并获取视频剪辑对象&#xff0c;然后通过剪辑对象获得视频时长。方法二&#xff1a;使用cv2库通过打开视频文件并获取帧率和总帧数两个属性&#xff0c;计算视频时长。 请…...