我正在学习C#中的事件/代表.我可以问一下你对我所选择的命名/编码风格的看法(取自Head First C#书)吗?
我明天正在和朋友一起教这个问题,并且正在尝试用最优雅的方式来解释这些概念.(认为理解一门学科的最好方法就是尝试教它!)
class Program
{
static void Main()
{
// setup the metronome and make sure the EventHandler delegate is ready
Metronome metronome = new Metronome();
// wires up the metronome_Tick method to the EventHandler delegate
Listener listener = new Listener(metronome);
metronome.OnTick();
}
}
Run Code Online (Sandbox Code Playgroud)
public class Metronome
{
// a delegate
// so every time Tick is called, the runtime calls another method
// in this case Listener.metronome_Tick
public event EventHandler Tick;
public void OnTick()
{
while (true) …Run Code Online (Sandbox Code Playgroud) 我最近创建了这两个(不相关的)方法来替换我的winforms应用程序中的大量样板代码.据我所知,他们工作正常,但我需要一些保证/建议,以确定是否存在一些我可能会遗漏的问题.
(从记忆里)
static class SafeInvoker
{
//Utility to avoid boiler-plate InvokeRequired code
//Usage: SafeInvoker.Invoke(myCtrl, () => myCtrl.Enabled = false);
public static void Invoke(Control ctrl, Action cmd)
{
if (ctrl.InvokeRequired)
ctrl.BeginInvoke(new MethodInvoker(cmd));
else
cmd();
}
//Replaces OnMyEventRaised boiler-plate code
//Usage: SafeInvoker.RaiseEvent(this, MyEventRaised)
public static void RaiseEvent(object sender, EventHandler evnt)
{
var handler = evnt;
if (handler != null)
handler(sender, EventArgs.Empty);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:请在此处查看相关问题
UPDATE
继死锁问题(在此问题中相关)之后,我已从Invoke切换到BeginInvoke(请参阅此处的解释).
另一个更新
关于第二个片段,我越来越倾向于使用'空委托'模式,通过使用空处理程序直接声明事件来解决"源"问题,如下所示:
event EventHandler MyEventRaised = delegate {};
Run Code Online (Sandbox Code Playgroud) 正如这个问题所示: 使用扩展方法提升C#事件 - 这很糟糕吗?
我正在考虑使用此扩展方法来安全地引发事件:
public static void SafeRaise(this EventHandler handler, object sender, EventArgs e)
{
if (handler != null)
handler(sender, e);
}
Run Code Online (Sandbox Code Playgroud)
但是Mike Rosenblum在Jon Skeet的回答中提出了这个问题:
你们需要将[MethodImpl(MethodImplOptions.NoInlining)]属性添加到这些扩展方法中,否则你可以通过JITter优化将代理复制到临时变量的尝试,从而允许空引用异常.
我在发布模式下做了一些测试,看看当扩展方法没有用NoInlining标记时是否可以获得竞争条件:
int n;
EventHandler myListener = (sender, e) => { n = 1; };
EventHandler myEvent = null;
Thread t1 = new Thread(() =>
{
while (true)
{
//This could cause a NullReferenceException
//In fact it will only cause an exception in:
// debug x86, debug x64 and release x86
//why doesn't it …Run Code Online (Sandbox Code Playgroud) 我见过的大多数代码都使用以下方式来声明和调用事件触发:
public class MyExample
{
public event Action MyEvent; // could be an event EventHandler<EventArgs>, too
private void OnMyEvent()
{
var handler = this.MyEvent; // copy before access (to aviod race cond.)
if (handler != null)
{
handler();
}
}
public void DoSomeThingsAndFireEvent()
{
// ... doing some things here
OnMyEvent();
}
}
Run Code Online (Sandbox Code Playgroud)
甚至ReSharper也会按照上面提到的方式生成一个调用方法.
为什么不这样做:
public class MyExample
{
public event Action MyEvent = delegate {}; // init here, so it's never null
public void DoSomeThingsAndFireEvent()
{
// ... doing …Run Code Online (Sandbox Code Playgroud) 例如,实现INotifyPropertyChanged接口:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Run Code Online (Sandbox Code Playgroud)
两件事情:
NullReferenceException但是现在,我们可以使用?.运算符进行空值检查.如果我使用它,Resharper是空闲的:

所以,问题是:ProperyChanged如果我使用空条件运算符,我应该将事件复制到局部变量吗?