声明event Action<>
和声明之间有什么不同吗event EventHandler<>
?
假设实际引发事件的对象无关紧要.
例如:
public event Action<bool, int, Blah> DiagnosticsEvent;
Run Code Online (Sandbox Code Playgroud)
VS
public event EventHandler<DiagnosticsArgs> DiagnosticsEvent;
class DiagnosticsArgs : EventArgs
{
public DiagnosticsArgs(bool b, int i, Blah bl)
{...}
...
}
Run Code Online (Sandbox Code Playgroud)
两种情况下的使用情况几乎相同:
obj.DiagnosticsEvent += HandleDiagnosticsEvent;
Run Code Online (Sandbox Code Playgroud)
关于event EventHandler<>
模式,有几件我不喜欢的事情:
更多代码意味着维护更多代码而没有任何明显的优势.
结果,我更喜欢 event Action<>
但是,只有在Action <>中有太多类型参数时,才需要额外的类.
Pau*_*hde 85
基于以前的一些答案,我将把我的答案分解为三个方面.
首先,使用Action<T1, T2, T2... >
vs使用派生类的物理限制EventArgs
.有三个:首先,如果您更改参数的数量或类型,则必须更改每个订阅的方法以符合新模式.如果这是第三方程序集将使用的面向公众的事件,并且事件args有可能发生变化,那么这将是使用从事件args派生的自定义类的一致性(记住,你仍然可以)使用a Action<MyCustomClass>
)其次,使用Action<T1, T2, T2... >
将阻止您将反馈BACK传递给调用方法,除非您有一些与Action一起传递的对象(例如,具有Handled属性).第三,你没有得到命名参数,所以如果你传递3 bool
的a int
,2 string
和a DateTime
,你不知道这些值是什么意思.作为旁注,您仍然可以"在使用时安全地触发此事件"方法Action<T1, T2, T2... >
.
其次,一致性影响.如果你有一个大型系统,你已经在使用,除了你有一个很好的理由之外,按照设计其余部分的方式几乎总是更好.如果您公开面对需要维护的事件,则替换派生类的能力可能很重要.记在脑子里.
第三,现实生活实践,我个人发现,我倾向于创建大量的一次性事件,比如我需要与之交互的属性更改(特别是在使用视图模型进行相互交互的MVVM时)或者事件有一个参数.大多数情况下,这些事件采取的形式public event Action<[classtype], bool> [PropertyName]Changed;
或public event Action SomethingHappened;
.在这些情况下,有两个好处.首先,我得到了一个发行类的类型.如果MyClass
声明并且是唯一触发事件的类,我会MyClass
在事件处理程序中获得一个显式实例.其次,对于诸如属性更改事件之类的简单事件,参数的含义是显而易见的,并在事件处理程序的名称中声明,我不必为这些类型的事件创建无数的类.
Fre*_*örk 66
主要的区别在于,如果你使用Action<>
你的事件将不会遵循系统中几乎任何其他事件的设计模式,我认为这是一个缺点.
主导设计模式(除了相同的力量之外)的一个优点是,您可以EventArgs
使用新属性扩展对象,而无需更改事件的签名.如果您使用过Action<SomeClassWithProperties>
,这仍然是可能的,但在这种情况下我没有真正看到不使用常规方法的观点.
Mar*_*ell 16
在大多数情况下,我会说遵循这种模式.我已经从中偏离,但很少,而对于具体原因.在这种情况下,我遇到的最大问题是我可能仍然使用Action<SomeObjectType>
,允许我稍后添加额外的属性,并使用偶尔的双向属性(想想Handled
,或其他反馈事件,其中订阅者需要在事件对象上设置属性.一旦你开始下线,你也可以使用EventHandler<T>
一些T
.
Pau*_*ich 14
当您的代码在300,000行项目中时,会出现wordier方法的优点.
像你一样使用动作,没有办法告诉我bool,int和Blah是什么.如果您的操作传递了定义参数的对象,那么就可以了.
使用需要EventArgs的EventHandler,如果您要使用针对其目的评论的属性的getter来完成DiagnosticsArgs示例,那么您的应用程序将更容易理解.另外,请在DiagnosticsArgs构造函数中注释或完全命名参数.
Sta*_*itz 10
我意识到这个问题已经有 10 多年的历史了,但在我看来,不仅最明显的答案没有得到解决,而且可能从问题中并不能很好地理解幕后发生的事情。此外,还有其他关于后期绑定的问题,以及这对于委托和 lambda 的意义(稍后会详细介绍)。
首先解决房间里 800 磅的大象/大猩猩,何时选择event
vs Action<T>
/ Func<T>
:
event
时,您用多条语句/ lambda表达式/功能,将执行(这是一个想多pub / sub模型的主要
区别了蝙蝠的权利)。作为事件的示例,让我们使用一个小型控制台应用程序连接一组简单且“标准”的事件,如下所示:
public delegate void FireEvent(int num);
public delegate void FireNiceEvent(object sender, SomeStandardArgs args);
public class SomeStandardArgs : EventArgs
{
public SomeStandardArgs(string id)
{
ID = id;
}
public string ID { get; set; }
}
class Program
{
public static event FireEvent OnFireEvent;
public static event FireNiceEvent OnFireNiceEvent;
static void Main(string[] args)
{
OnFireEvent += SomeSimpleEvent1;
OnFireEvent += SomeSimpleEvent2;
OnFireNiceEvent += SomeStandardEvent1;
OnFireNiceEvent += SomeStandardEvent2;
Console.WriteLine("Firing events.....");
OnFireEvent?.Invoke(3);
OnFireNiceEvent?.Invoke(null, new SomeStandardArgs("Fred"));
//Console.WriteLine($"{HeightSensorTypes.Keyence_IL030}:{(int)HeightSensorTypes.Keyence_IL030}");
Console.ReadLine();
}
private static void SomeSimpleEvent1(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent1)}:{num}");
}
private static void SomeSimpleEvent2(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent2)}:{num}");
}
private static void SomeStandardEvent1(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent1)}:{args.ID}");
}
private static void SomeStandardEvent2(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent2)}:{args.ID}");
}
}
Run Code Online (Sandbox Code Playgroud)
输出将如下所示:
如果你对Action<int>
or做同样的事情Action<object, SomeStandardArgs>
,你只会看到SomeSimpleEvent2
and SomeStandardEvent2
。
那么里面发生了event
什么?
如果我们展开FireNiceEvent
,编译器实际上会生成以下内容(我省略了一些与本讨论无关的线程同步细节):
private EventHandler<SomeStandardArgs> _OnFireNiceEvent;
public void add_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Combine(_OnFireNiceEvent, handler);
}
public void remove_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Remove(_OnFireNiceEvent, handler);
}
public event EventHandler<SomeStandardArgs> OnFireNiceEvent
{
add
{
add_OnFireNiceEvent(value)
}
remove
{
remove_OnFireNiceEvent(value)
}
}
Run Code Online (Sandbox Code Playgroud)
编译器生成一个私有委托变量,该变量对生成它的类命名空间不可见。该委托用于订阅管理和后期绑定参与,面向公众的界面是我们都熟悉和喜爱的熟悉+=
和-=
运营商:)
您可以通过将FireNiceEvent
委托范围更改为 protected 来自定义添加/删除处理程序的代码。这现在允许开发人员向钩子添加自定义钩子,例如日志记录或安全钩子。这确实产生了一些非常强大的功能,现在允许根据用户角色等定制订阅的可访问性。你能用 lambda 来做到这一点吗?(实际上您可以通过自定义编译表达式树,但这超出了本响应的范围)。
从这里的一些回复中解决几点:
更改 args 列表中的 args 列表Action<T>
和更改从EventArgs
. 要么不仅需要编译更改,它们都将更改公共接口并需要版本控制。没有不同。
关于哪个是行业标准,这取决于它在何处使用以及为什么使用。Action<T>
此类常用于 IoC 和 DI,event
常用于消息路由,如 GUI 和 MQ 类型的框架。请注意,我经常说,并非总是如此。
委托与 lambda 的生命周期不同。人们还必须意识到捕获......不仅仅是关闭,而且还有“看看猫拖进来的东西”的概念。这确实会影响内存占用/生命周期以及管理(又名泄漏)。
还有一件事,我之前提到过的……延迟绑定的概念。在使用像 LINQ 这样的框架时,您会经常看到这一点,关于 lambda 何时变为“活动”。这与委托的后期绑定非常不同,后者可以发生不止一次(即 lambda 总是在那里,但绑定根据需要随时发生),而不是 lambda,一旦发生,它就完成了-- 魔法消失了,方法/属性将始终绑定。要记住的事情。
如果您遵循标准事件模式,则可以添加扩展方法以使事件触发检查更安全/更容易.(即,下面的代码添加了一个名为SafeFire()的扩展方法,它执行空检查,以及(显然)将事件复制到一个单独的变量中,以防止可能影响事件的常见空竞争条件.)
(虽然我是否有两种想法,你是否应该在null对象上使用扩展方法......)
public static class EventFirer
{
public static void SafeFire<TEventArgs>(this EventHandler<TEventArgs> theEvent, object obj, TEventArgs theEventArgs)
where TEventArgs : EventArgs
{
if (theEvent != null)
theEvent(obj, theEventArgs);
}
}
class MyEventArgs : EventArgs
{
// Blah, blah, blah...
}
class UseSafeEventFirer
{
event EventHandler<MyEventArgs> MyEvent;
void DemoSafeFire()
{
MyEvent.SafeFire(this, new MyEventArgs());
}
static void Main(string[] args)
{
var x = new UseSafeEventFirer();
Console.WriteLine("Null:");
x.DemoSafeFire();
Console.WriteLine();
x.MyEvent += delegate { Console.WriteLine("Hello, World!"); };
Console.WriteLine("Not null:");
x.DemoSafeFire();
}
}
Run Code Online (Sandbox Code Playgroud)
查看我们发现的标准 .NET 事件模式
.NET 事件委托的标准签名是:
void OnEventRaised(object sender, EventArgs args);
[...]
参数列表包含两个参数:sender和事件参数。sender 的编译时类型是 System.Object,即使您可能知道一个始终正确的派生类型。按照惯例,使用object。
在同一页面下方,我们找到了一个典型事件定义的示例,类似于
public event EventHandler<EventArgs> EventName;
Run Code Online (Sandbox Code Playgroud)
如果我们定义
class MyClass
{
public event Action<MyClass, EventArgs> EventName;
}
Run Code Online (Sandbox Code Playgroud)
处理程序可能是
void OnEventRaised(MyClass sender, EventArgs args);
Run Code Online (Sandbox Code Playgroud)
wheresender
有正确的(更派生的)类型。
归档时间: |
|
查看次数: |
86940 次 |
最近记录: |