为什么EventArgs不是一个接口而不是一个类?

时间:2017-01-04 16:24:55

标签: c# .net

回到过去他们设计一种提升和处理事件的共同风格时,为什么他们决定让EventArgs成为一个班级?它完全是空的,没有任何通用代码,为什么它不是一个接口呢?

空类与空接口相比有什么优势?

通过使它成为一个类,它们采用了类的所有限制(即没有多重继承),而没有利用它的任何好处(即提供一些常见的行为)。

他们为什么不把这样的约会变成这样?有一个带有空接口参数的委托,该接口的实现表示没有额外信息的事件。

public interface IEventArgs { }

public class EventArgs : IEventArgs
{
    public static EventArgs Empty { get { return _empty; } }
    private static EventArgs _empty = new EventArgs();
}

public delegate void EventHandler(object sender, IEventArgs e);
public delegate void EventHandler<T>(object sender, T e) where T : IEventArgs;

实施例

假设你有两个既定惯例的事件:

public event EventHandler<CancelEventArgs> ReceivingMessage;
public event EventHandler<StringEventArgs> DroppingMessage;

public class StringEventArgs : EventArgs
{
    public StringEventArgs(string data)
    {
        Data = data;
    }

    public string Data { get; private set; }
}

现在假设您想要将DroppingMessage和字符串数据的取消功能添加到ReceivingMessage事件处理程序。除非你想让它成为一个重大改变,否则这是唯一的方法:

public event EventHandler<CancelStringEventArgs> ReceivingMessage;
public event EventHandler<StringCancelEventArgs> DroppingMessage;

public class CancelStringEventArgs : CancelEventArgs
{
    public CancelStringEventArgs(string data)
    {
        Data = data;
    }

    public string Data { get; private set; }
}

public class StringCancelEventArgs : StringEventArgs
{
    public StringCancelEventArgs(string data) : base(data) { }

    public bool Cancel { get; set; }
}

public class StringEventArgs : EventArgs
{
    public StringEventArgs(string data)
    {
        Data = data;
    }

    public string Data { get; private set; }
}

现在,您有两个具有相同行为和重复代码的类,您必须深入研究词库,以考虑新的和创造性的方法来区分这些相同的类。另外,你让你的用户诅咒你的遗产(或继承,原谅双关语)因为他们必须在同一类中意识到这些差异。

另一方面,接口只是意味着采用这个原始代码......

public event EventHandler<ICancelEventArgs> ReceivingMessage;
public event EventHandler<IStringEventArgs> DroppingMessage;

public interface IStringEventArgs : IEventArgs
{
    string Data { get; }
}
public class StringEventArgs : IStringEventArgs
{
    public StringEventArgs(string data)
    {
        Data = data;
    }
    public string Data { get; private set; }
}

...并添加一些轻微的不间断更改:

public event EventHandler<IStringCancelEventArgs> ReceivingMessage;
public event EventHandler<IStringCancelEventArgs> DroppingMessage;

public interface IStringEventArgs : IEventArgs
{
    string Data { get; }
}
public interface IStringCancelEventArgs : IStringEventArgs, ICancelEventArgs { }
public class StringEventArgs : IStringCancelEventArgs
{
    public StringEventArgs(string data)
    {
        Data = data;
    }
    public string Data { get; private set; }
    public bool Cancel { get; set; }
}

作为旁注,将其指定为接口还允许在符合约定的事件的通用接口中进行协方差。然而,当时没有接口的这个功能,所以我明白为什么它没有被考虑在内。虽然它仍然让我感到悲伤。 :(

3 个答案:

答案 0 :(得分:2)

使用.NET 4.5(http://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.110))删除了TEventArgs EventArgs作为基类的限制。所以现在它与您希望的完全相同。

但我认为这样做是因为在这种情况下界面无用。 EventArgsIEventArgs只是开发人员轻松发现事件数据类的标记。即使它是一个界面,你也不会使用它。由于您的订阅者希望从IEventArgs继承的具体最终类型,而不是接口本身。所以这个接口只会添加不必要的附加类型声明,因为无论如何都需要EventArgs来模拟空数据。

答案 1 :(得分:2)

我现在只能推测(我现在无法访问带注释的基类库的副本)。我的主要原因是定义一个继承链。是的,EventArgs充当基础,并不提供任何功能。现在,检查CancelEventArgs。它确实提供了一些功能(即,提供了Cancel属性和一个额外的类构造函数)。但是,此类还充当许多子类的基类,例如DoWorkEventArgs

本质上,基类EventArgs类只是作为所有事件参数的公共可靠基类。您可以编写只需要考虑中间类属性的代码(如Cancel)。

答案 2 :(得分:2)

除了其他答案:EventArgs并非完全没有任何功能 - 它提供了静态EventArgs.Empty成员,这是一种统一的方式来提升没有数据的事件。