我最近在使用一个DateTime对象,并写了这样的东西:
DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?
Run Code Online (Sandbox Code Playgroud)
intellisense文档AddDays()说它增加了一天的日期,它没有 - 它实际上返回添加了一天的日期,所以你必须写如下:
DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date
Run Code Online (Sandbox Code Playgroud)
这个曾经多次咬过我,所以我认为编制最糟糕的C#陷阱会很有用.
C#4.0协同和逆变支持的一些奇怪行为:
using System;
class Program {
static void Foo(object x) { }
static void Main() {
Action<string> action = _ => { };
// C# 3.5 supports static co- and contravariant method groups
// conversions to delegates types, so this is perfectly legal:
action += Foo;
// since C# 4.0 much better supports co- and contravariance
// for interfaces and delegates, this is should be legal too:
action += new Action<object>(Foo);
}
}
Run Code Online (Sandbox Code Playgroud)
这是结果 ArgumentException: Delegates must be of the …
.NET中的事件有一个标准模式 - 它们使用一个delegate类型,它接受一个名为sender的普通对象,然后是第二个参数中的实际"payload",它应该从中派生出来EventArgs.
派生的第二个参数的基本原理EventArgs似乎非常清楚(请参阅.NET Framework标准库带注释的参考).它旨在确保随着软件的发展,事件接收器和源之间的二进制兼容性.对于每个事件,即使它只有一个参数,我们派生一个自定义事件参数类,它具有包含该参数的单个属性,这样我们就可以保留在未来版本中向有效负载添加更多属性而不破坏现有客户端代码的能力. .在独立开发组件的生态系统中非常重要.
但我发现零参数也是如此.这意味着如果我的第一个版本中有一个没有参数的事件,我会写:
public event EventHandler Click;
Run Code Online (Sandbox Code Playgroud)
......然后我做错了.如果我将来的委托类型更改为新的类作为其有效负载:
public class ClickEventArgs : EventArgs { ...
Run Code Online (Sandbox Code Playgroud)
...我将破坏与客户的二进制兼容性.客户端上界到内部方法的具体超载add_Click是需要EventHandler的,如果我改变了委托类型,然后他们无法找到超载,所以有一个MissingMethodException.
好的,那么如果我使用方便的通用版本怎么办?
public EventHandler<EventArgs> Click;
Run Code Online (Sandbox Code Playgroud)
不,仍然是错的,因为一个EventHandler<ClickEventArgs>不是EventHandler<EventArgs>.
因此,为了获得好处EventArgs,您必须从中获取,而不是直接使用它.如果你不这样做,你也可以不使用它(在我看来).
然后是第一个论点,sender.在我看来,这似乎是一个邪恶的耦合配方.事件触发本质上是一个函数调用.一般来说,这个函数是否应该能够通过堆栈挖掘并找出调用者是谁,并相应地调整其行为?我们应该强制要求接口看起来像这样吗?
public interface IFoo
{
void Bar(object caller, int actualArg1, ...);
}
Run Code Online (Sandbox Code Playgroud)
毕竟,实现者Bar可能想知道是谁caller,所以他们可以查询更多信息!我希望你现在正在呕吐.为什么事件会有所不同?
因此,即使我已经准备好为EventArgs我声明的每个事件都要为一个独立的派生类而烦恼,只是为了让它值得我在使用EventArgs时,我绝对宁愿删除对象发送者参数.
Visual Studio的自动完成功能似乎并不关心您用于事件的委托 - 您可以键入+= [命中空间,返回]并为您编写一个匹配任何委托的处理程序方法.
那么偏离标准模式我会失去什么价值? …
该文章陈述如下:http://msdn.microsoft.com/en-us/library/dd799517.aspx
差异不适用于代表组合.也就是说,给定两个类型的委托Action<Derived>和Action<Base>(Action(Of Derived)和Action(Of Base)在Visual Basic中),您不能将第二个委托与第一个委托合并,尽管结果是类型安全的.Variance允许将第二个委托分配给类型变量Action<Derived>,但委托只有在其类型完全匹配时才能组合.
Action<B> baction = (taret) => { Console.WriteLine(taret.GetType().Name); };
Action<D> daction = baction;
Action<D> caction = baction + daction;
Run Code Online (Sandbox Code Playgroud)
在上面的代码baction和daction采取不同的参数.但我仍然可以将它们结合起来.我错过了什么?
TIA.