【问题标题】:Event Inheritance with C#8 Default Interface Implementation/Traits具有 C#8 默认接口实现/特征的事件继承
【发布时间】:2019-11-11 18:06:11
【问题描述】:

目前几乎没有关于新 C#8 默认接口实现(特征)的事件限制的文档。我对the spec proposal. 感到特别困惑,不仅该示例给出了无效的 C#(“覆盖”事件缺少标识符),而且在 C#8(VS2019、.NET Core 3.0)中实现这些中的任何一个都会返回大量编译器例外。此外,the release notes for C#8 没有提及接口特征的事件。当我继续尝试寻找答案时,我也无法从 the open issues list. 收集到任何有用的信息

所以问题是:此功能是否已实现且可用?如果是这样,正确的语法是什么?

【问题讨论】:

  • 您不能将事件与 DIM 一起使用。我敢打赌你想实现你自己的 INPC 特质?
  • 它纯粹是一个重构功能,有助于处理必须在界面中进行更改但无法修改外部代码的问题。事件没有意义,这样的代码永远不会引发事件。
  • @HansPassant 这不仅与版本有关,还与特征有关。好的,真正的 原因是与使用默认成员的 Android SDK 兼容。 INPC 特征是有道理的,但现在不可能。我敢打赌,如果 Java 有事件,它也会被实现
  • 绝对不是拼写错误或无法复制的东西。 It's a known issue,并在language design meetings中间接提及
  • @PanagiotisKanavos 我做到了,哈哈。但是没有成功:(

标签: c# .net-core c#-8.0 default-interface-member


【解决方案1】:

默认接口成员用于特征,而不仅仅是版本控制,INPC 特征是有意义的。

不幸的是,现在还不能使用 DIM 来引发事件,并实现这个 seems to be a pain - 这需要彻底检查事件机制并破坏大量代码,尤其是库代码。我们可以使用 DIM 来添加或删除处理程序,但这不是很有用。

如果有类似的东西就好了:

interface InpcTrait : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private T Set(T value,String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        return value;
    }
}

class Customer
{
    private string _name;
    public string Name 
    {
        get=>_name;
        set=>_name=Set(value,"Name");
    }
}


很遗憾,这是不可能的。这是因为类中的event 关键字会生成一个支持字段,其中包含事件处理程序 和添加/删除访问器。当我们引发事件时,我们调用该事件处理程序。

接口不能有状态,这意味着我们不能访问那个事件来引发它。

当我们在接口中指定一个事件时,我们会创建一个虚拟事件,编译器只允许向它添加/删除事件处理程序。提升接口仍然需要访问支持字段。

This Sharplab.io example 表明:

public class DemoCustomer : INotifyPropertyChanged
{
    // These fields hold the values for the public properties.
    private Guid idValue = Guid.NewGuid();
    private string customerNameValue = String.Empty;
    private string phoneNumberValue = String.Empty;

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

生成

    [CompilerGenerated]
    private PropertyChangedEventHandler m_PropertyChanged;

    public event PropertyChangedEventHandler PropertyChanged
    {
        [CompilerGenerated]
        add
        {
            //some code
        }
        [CompilerGenerated]
        remove
        {
            //some code
        }
    }

    private void NotifyPropertyChanged(string propertyName = "")
    {
        if (this.m_PropertyChanged != null)
        {
            this.m_PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

我们可以做的是添加或删除事件处理程序,但我们甚至无法检查事件是否已经有其他处理程序。我们冒着多次添加相同事件处理程序的风险。

这是有效的:

interface INPCtrait:System.ComponentModel.INotifyPropertyChanged
{            
    private  void AddSomeDefaultHandler()
    {
       PropertyChanged+=Something;
    }

    private  void RemoveDefaultHandler()
    {
       PropertyChanged-=Something;
    }

    public void Something(Object sender,System.ComponentModel.PropertyChangedEventArgs args)
    {
    }    
}

但我们无法知道是否需要添加该默认处理程序。

【讨论】:

  • 啊,有道理,谢谢!您确实是正确的,INPC 是我正在寻找的用例。避免强制继承会很好,但听起来坚持使用抽象基类将继续成为前进的标准。
猜你喜欢
  • 1970-01-01
  • 2020-04-20
  • 2016-02-12
  • 1970-01-01
  • 1970-01-01
  • 2015-03-23
  • 2016-10-23
  • 2020-07-01
  • 1970-01-01
相关资源
最近更新 更多