- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
委托 是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容 参数 和 返回类型 的方法进行 绑定 。 你可以通过委托实例调用方法.
简单的理解,委托是方法的抽象类,它定义了方法的类型,可以实例化。和普通的类一样, 可以申明 变量 进行赋值,可以当作 参数 传递,可以定义成 属性.
委托具有以下属性:
使用 delegate 关键字,定义具体的委托类型,Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型.
namespace ConsoleApp.DelegateTest
{
//例:表示无参数,无返回。
public delegate void MethodtDelegate();
//例:表示有两个参数,并返回int型。
public delegate int MethodtDelegate(int x, int y);
}
方法绑定,进行调用 。
static void Main(string[] args)
{
MethodtDelegate methodt = Test;
//例1:直接调用
methodt(1,2);
//例2:假设作为参数传递,进行调用。比如回调函数场景
InvokeTest(methodt);
}
public static int Test(int a, int b)
{
return a + b;
}
public static void InvokeTest(MethodtDelegate methodt)
{
//以下两种方式都可以调用
var sum = methodt(1, 2);
var sum = methodt.Invoke(1, 2);
}
抽象的 Delegate 类提供用于松散耦合和调用的基础结构,但是这样看来,引发一个问题,无论何时需要不同的方法参数,这都会创建新的委托类型。 一段时间后此操作可能变得繁琐。 每个新功能都需要新的委托类型,幸运的是,没有必要这样做,框架已经帮我们定义 Action 和 Func 类,我们可以直接申明进行使用 。
Action 是无返回值的泛型委托。 Action 委托的变体可包含多达 16 个参数,如 Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16> 。 重要的是这些定义对每个委托参数使用不同的泛型参数,这样可以具有最大的灵活性。框架源码,如图:
使用就很方便了,我们只需要直接申明委托类型进行使用,例:
//例:表示有传入参数int,string,bool无返回值的委托
Action<int,string,bool>
Func 委托的变体可包含多达 16 个输入参数,如 Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult> 。 按照约定,返回结果的类型始终是所有 Func 声明中最后一个参数的类型,利用 out 类型参数实现.
Func 是有返回值的泛型委托,func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void。框架源码,如下:
使用就很方便了,我们只需要直接申明委托类型进行使用,例:
//表示无参,返回值为int的委托,
Func<int>
//表示传入参数为object, string 返回值为int的委托
Func<object,string,int>
我这里就做一个多播案例,帮助大家理解,其实.NET core 日志框架和其他第三方日志框架,差不多就是这种套路 。
这个类我们的定义好委托和调用委托的方法.
public static class Logger
{
public static Action<string> WriteMessage;
public static void LogMessage(string msg)
{
WriteMessage(msg);
}
}
一个写入文件的,文件记录器 。
public class FileLogger
{
public FileLogger()
{
Logger.WriteMessage += LogMessage;
}
public void DetachLog() => Logger.WriteMessage -= LogMessage;
// make sure this can't throw.
private void LogMessage(string msg)
{
try
{
Console.WriteLine($"FileLogger\t{msg}");
}
catch (Exception)
{
// Hmm. We caught an exception while
// logging. We can't really log the
// problem (since it's the log that's failing).
// So, while normally, catching an exception
// and doing nothing isn't wise, it's really the
// only reasonable option here.
}
}
}
一个写入不同数据库的,数据库记录器 。
public class DBLogger
{
private readonly string name;
public DBLogger(string name)
{
this.name = name;
Logger.WriteMessage += LogMessage;
}
public void DetachLog() => Logger.WriteMessage -= LogMessage;
// make sure this can't throw.
private void LogMessage(string msg)
{
try
{
Console.WriteLine($"DBLogger{name}\t{msg}");
}
catch (Exception)
{
// Hmm. We caught an exception while
// logging. We can't really log the
// problem (since it's the log that's failing).
// So, while normally, catching an exception
// and doing nothing isn't wise, it's really the
// only reasonable option here.
}
}
}
以上两个代码逻辑,博主就不介绍了,就用一个控制台输出,代表业务代码了 。
测试一下,广播和委托删除效果 。
static void Main(string[] args)
{
//添加一个文件记录器和两个数据库记录器
new FileLogger();
new DBLogger("DB1");
var a = new DBLogger("DB2");
//调用委托
Logger.LogMessage("add失败");
//删除此数据库记录器
a.DetachLog();
Console.WriteLine("======DetachLogDB2========");
//调用委托
Logger.LogMessage("add失败");
}
运行效果:
在实际项目中,大家就自行发挥 。
说到委托,博主也把这个重要的知识点讲解一下,这个知识点很多人可能不知道或者踩过坑,但掌握了这个知识点其实可以实现一些比较花哨功能.
这里博主就用一个案例进行体现变量捕获,这里代码博主就用 lambda 表达式 进行简写,不太熟悉的可以通过链接跳转进行学习.
逻辑就是,简单的累计一下数量,通过最终的值体现。这里博主分别申明两个整数型变量,通过两个委托分别累计,然后看各自的值。两个委托区别就是传值方式的不同.
static void Main(string[] args)
{
int count1 = 0;//委托1的参数
int count2 = 0;//委托2的参数
//实例化委托1
Action<int> action1 = (p) =>
{
p++;
Console.WriteLine("action1:" + p);
};
//实例化委托2
Action action2 = () =>
{
count2++;
Console.WriteLine("action2:" + count2);
};
//循环5此
for (int i = 0; i < 5; i++)
{
action1(count1);//调用委托1
action2();//调用委托2
Console.WriteLine("---------------------------分割线");
}
Console.WriteLine("count1 最终值:" + count1);
Console.WriteLine("count2 最终值:" + count2);
}
测试效果:
大家发现没?逻辑代码一下,只是参数传递方式不一样,结果截然不同:
委托1的方式:不改变变量的值,方法之间是 不共享 这个参数的。这种很容易理解,就和我们调用普通方法一样,变量是值类型,是拷贝了一个副本传给了方法进行使用 。
委托2的方式:改变变量的值,方法之间是 共享 这个参数的。这种就像引用类型参数一样,是不是很神奇,难道是利用了 ref 关键字实现的?
其实没有大家想学的那么神秘,委托之所以使用方式和类无异,是因为它本身就是一个类,只是这个过程的定义由 编译器 帮我们做了,我们只需要使用C#的语法糖。接下来博主就带大家揭开委托的神秘面纱.
博主就简单写了一个委托,然后通过IL DASM工具查看IL代码 。
internal class Program
{
static void Main(string[] args)
{
int b = 888888888;
Func<int> action = () =>
{
return b++;
};
var a = action.Invoke();
}
}
internal class Program
{
public class DisplayClass
{
public int b;
public int Invoke()
{
return b++;
}
}
public class _Func<T>
{
private readonly DisplayClass displayClass;
public _Func(DisplayClass display)
{
displayClass = display;
}
public T Invoke()
{
object b = displayClass.Invoke();
return (T)b;
}
}
static void Main(string[] args)
{
var display = new DisplayClass();
display.b = 888888888;
var actionTest = new _Func<int>(display);
var a = actionTest.Invoke();
}
}
。
大家发现没,最终的IL代码一模一样。也就说,委托就是编译器帮我们把func编译成一个带invoke函数的func类和生成一个装捕获的变量和函数体的类,然后通过构造函数将对象引用和函数指针(获取指针就是大家所说的把非托管指针压入当前 栈 )传给func类的实例化。然后最终调用的时候,委托类的invoke函数会去调用真正的函数。就这样完成了对函数的抽象.
现在大家是不是对委托有了一定的理解了,而委托涉及到的捕获变量和参数变量,生命周期就说得通了,也知道为啥委托改变了变量,能通知到原本的变量,因为对变量就行了类的装箱,打包成了一个一个引用类型,那方法外部当然知道变量的值被改变了,因为大家都是拿着引用对象的地址呀。下面做个生命周期小总结:
其实讲完委托,事件就很容易理解了, 博主就简单讲解一下,如果大家有需要,博主就再写一篇详细的讲解.
事件:实际上,事件是建立在对委托的语言支持之上的一种设计而已.
/定义一个委托
4 public delegate void delegateRun();
5 //定义一个事件
6 public event delegateRun eventRun;
简单的说,事件可以看作是一个委托类型的变量 。
它们都提供了一个后期绑定方案:在该方案中,组件通过调用仅在运行时识别的方法进行通信。 它们都支持单个和多个订阅服务器方法。 也就是单播和多播支持。 二者均支持用于添加和删除处理程序的类似语法。 最后,引发事件和调用委托使用完全相同的方法调用语法。 它们甚至都支持与 ?. 运算符一起使用的相同的 Invoke() 方法语法.
public event EventHandler<NewMailEventArgs> NewMail;
可以看到当我们定义一个NewEvent时,编译器帮我们生成了:1. 一个private NewMail 字段,类型为 EventHandler<NewMailEventArgs>。 2.一个 add_NewMail 方法,用于将委托添加到委托链(内部调用了Delegate.Combine方法)。3.一个 remove_NewMail 方法,用于将委托从委托链移除(内部调用了Delegate.Remove方法)。对事件的操作,就是是对NewMail字段的操作.
主要区别就是:
1.事件处理程序通过修改事件参数对象的属性将信息传回到事件源。 虽然这些惯用语可发挥作用,但它们不像从方法返回值那样自然.
2.包含事件的类以外的类只能添加和删除事件侦听器;只有包含事件的类才能调用事件。 事件通常是公共类成员。 相比之下,委托通常作为参数传递,并存储为私有类成员(如果它们全部存储) 。
3.当事件源将在很长一段时间内引发事件时,基于事件的设计会更加自然。比如基于事件的 UI 控件设计案例 。
总结:
(1)事件:事件时属于类的成员,所以要放在类的内部.
(2)委托:属于一个定义,是和类、接口类似的,通常放在外部.
所以事件这种架构设计思想还是很值得大家去学习的.
所以说,如果你的代码在不调用任何订阅服务器的情况下可完成其所有工作,使用基于事件的设计会更好点.
大家在项目中,怎么进行选择,就看实际需求了.
看到这里的朋友,肯定对委托和事件还是有了一定的了解了,毕竟博主很用心的在写,尽量讲细一点。如果大家觉得博主讲解的比较全面,且透彻。大家可以点点赞,给予鼓励。也可以关注博主后续的更新,每一篇都会尽心讲解 。
。
最后此篇关于C#委托原理刨析、事件原理刨析,外加两者对比的文章就讲到这里了,如果你想了解更多关于C#委托原理刨析、事件原理刨析,外加两者对比的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!