我最近开始研究 C# 中的事件主题,这是窗口应用程序之前的最后一个主题。在解释事件格式化约定时,老师多次澄清,约定本身和其中的各个解决方案(特别是同一个发送者)同时存在的原因是,在其创建时既没有 LINQ 也没有 lambda在 C# 表达式中,现在在他们的帮助下,可以更有效地解决相同的问题(链接到原始来源)。
由于我正在学习的课程已经很老了(创建了大约10年),所以我有一个问题:LINQ和lambda表达式的出现是否影响了C#中设计事件的规则,以及约定本身仍然存在吗?存在于他们的设计中,或者是否可以充分使用 LINQ 和委托?)提前致谢!
看。
首先,标准事件不是在 C# 级别定义的,也不是在 WinForms 级别定义的。创建事件的准则是在 .NET 本身中发明的,它们应该适用于 .NET 支持的所有语言:C#、Visual Basic 以及 IronPython 等外来语言(这里有完整列表)。
.NET 框架不依赖于 C#,并且无权假设您的语言将具有方便的闭包。然而,在采用该约定时,C# 还没有闭包。
(当然,LINQ 与事件的定义无关:如果没有 LINQ,一切都会一样。)
但在您自己的代码中,您可以根据需要定义事件,例如如下所示:
但要做好心理准备,习惯于按照指导方针进行定义的同事可能会大吃一惊,而你将不得不为你的决定辩护。
C# 中是否仍然需要这个旧约定,还是应该将其视为过时的约定?有时你需要它。
当您编写 UI 代码时,您可能会浪费资源,因为代码不经常执行。
但是,如果您的代码级别较低,那么使用 lambda 会有一个特殊之处:捕获变量的每个 lambda 都是一个在堆上分配的新对象,会给垃圾收集器带来一点负载。对于经常或多次执行的代码来说,这可能是一个真正的问题,而且很多时候“一点”会变成“很多”。在这种情况下,您将希望以老式方式创建一个通用方法,该方法将订阅事件,并从 和 获取您的上下文(即附加信息,如示例中的月份名称
sender)args。顺便说一下,lambda 不一定分配在堆上。如果 lambda 没有捕获任何变量,那么编译器就足够聪明,可以将其转换为方法。为了确保您的 lambda 不会捕获任何内容,他们甚至想出了一个特殊的修饰符
static(static x => x * x)。但是这样的 lambda 表达式当然无法捕获状态,您必须再次使用sender/将上下文“传递”给方法args。应该如何正确识别事件?没有“正确”的方法;每个程序员(如果你在一个团队中工作,则每个团队)都自己决定。
您可以遵循.NET指南,那么您的代码将会更加熟悉。您可以忽略准则并定义事件,仅提供处理程序绝对必要的信息,在这种情况下,使用事件的代码将更加方便。由您(您的团队)决定。
如果您正在编写共享库,那么遵循指南是有意义的,以避免给用户带来意外。如果您正在编写属于较大框架一部分的代码(例如,WPF 或 WinForms 应用程序的 View 部分),则以与代码其余部分相同的方式定义事件是有意义的(即,根据到指南)。如果您编写较低级别的代码,如果您愿意,可以使用非标准方法。
关于建议:
C #允许您生成任何种类的事件,但为了软件组件兼容性,您应该遵循Microsoft制定的准则。这些准则归结为以下要求:事件处理程序必须有两个参数。第一个是对生成事件的对象的引用,第二个是类型参数,
EventArgs其中包含处理程序所需的有关事件的任何附加信息。因此,.NET 兼容的事件处理程序应具有以下一般形式:отправитель是调用代码使用关键字传递的参数this。данныеСобытия类型参数EventArgs(或从它派生的类型)包含有关事件的附加信息,如果不需要,可以忽略。该类EventArgs不包含可用于将附加数据传递给处理程序的字段。该类EventArgs充当基类,从中派生出包含所有必需字段的派生类。还有EventArgs一个static字段Empty,它是一个没有数据类型的对象EventArgs。代码示例(在.NET 8上测试):
视频中似乎是一个正常人,他说话很好,语速也很快,但他说的是暴风雪。
Linq和 lambda”与 Windows 窗体控件没有直接关系。在示例中,它们用于其预期目的 - 处理枚举 (IEnumerable)。特别是,使用这些函数,作者从字符串数组中获取了一个类型的对象数组MenuItem。(sender,args)=>CreateReport(name)- 这是一个与 Click 事件处理程序的签名匹配的 lambda,即它有两个适当类型的参数。 Studio 将显示参数的类型sender为 -object和args-EventArgs。并且没有其他选择会起作用。也就是说,它不会sender去任何地方。这些参数在 lambda 中被简单地忽略。顺便说一句,现在可以写成(_, _) => CreateReport(name)。sender?这样事件处理程序就可以访问调用该处理程序的对象。由于处理程序是控制元素外部的方法,因此只能通过这种方式将有关该控制元素的信息传递给它。需要这些信息吗?显然这是需要的。Select为集合的每个元素调用的方法中monthNames,即调用 12 次,因此在运行时使用闭包变量的值创建了 12 个辅助对象,并且Click数组的每个元素的事件menuItems接收对其单个对象的 lambda 方法。也就是说,在Linqlambda 表达式的帮助下,他自动创建了处理程序。但当他创建它时MenuItemSelected,它确实成为 12 个菜单命令的唯一处理程序。处理程序从参数中获取月份的名称sender。正确的选项是第二个,并且可以删除附加方法:
如果每个菜单命令都需要一些附加信息,则继承所有 Windows 窗体控件的 Control 类将提供一个 Tag 属性,您可以在其中记录对具有附加信息的对象的引用。
sender是OOP范式之一——封装。参见第 3 点。Click在没有它的情况下在处理程序中更改其状态,并且无法使用 lambda 来扭曲它。senderPS为什么需要它
EventArgs?您将看到比菜单命令更复杂的东西,例如DataGridView。让我们以CellClick事件为例,该事件在从该类派生的对象中EventArgs告诉处理程序它发生在哪个单元格上Click。