为了引发事件,我们使用OnEventName方法,如下所示:
protected virtual void OnSomethingHappened(EventArgs e)
{
EventHandler handler = SomethingHappened;
if (handler != null)
{
handler(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
但这个有什么不同?
protected virtual void OnSomethingHappened(EventArgs e)
{
if (SomethingHappened!= null)
{
SomethingHappened(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
显然第一个是线程安全的,但为什么以及如何?
没有必要开始一个新线程?
关于接受的答案的快速说明:我不同意Jeffrey的一小部分答案,即Delegate必须是一个引用类型,因此所有代表都是引用类型.(多层继承链排除值类型并不是真的;例如,所有枚举类型都System.Enum继承自System.ValueType,而继承自所有引用类型的继承System.Object,但是我认为事实是从根本上说,所有代表事实上都不仅仅是继承而是来自这里的关键实现.正如雷蒙指出,在一个评论他的答案,一旦你承诺支持多用户,有真的没有一点不使用引用类型的代表本身,因为需要一个数组的地方.DelegateMulticastDelegate
请参阅底部的更新.
如果我这样做,我一直觉得很奇怪:
Action foo = obj.Foo;
Run Code Online (Sandbox Code Playgroud)
我每次都在创建一个新 Action对象.我确信成本很低,但它涉及到内存分配以便以后进行垃圾回收.
鉴于委托本身就是不可变的,我想知道为什么它们不能成为价值类型?然后像上面那样的一行代码只会对堆栈上的内存地址进行简单的赋值*.
即使考虑匿名功能,似乎(对我而言)这也行得通.请考虑以下简单示例.
Action foo = () => { obj.Foo(); };
Run Code Online (Sandbox Code Playgroud)
在这种情况下foo确实构成了一个闭包,是的.在许多情况下,我想这确实需要一个实际的引用类型(例如当局部变量被关闭并在闭包内被修改时).但在某些情况下,它不应该.例如,在上面的例子中,似乎支持闭包的类型看起来像这样: 我收回了我对此的原始观点.下面确实需要一个引用类型(或:它并不需要是的,但如果它是一个struct只是要它来获得反正盒装).所以,忽略下面的代码示例.我留下它只是为了提供具体提及它的答案的背景.
struct CompilerGenerated
{
Obj obj;
public CompilerGenerated(Obj obj)
{
this.obj = obj;
}
public void CallFoo()
{
obj.Foo();
}
}
// …Run Code Online (Sandbox Code Playgroud)