暴露继承的事件

时间:2019-04-09 17:53:32

标签: c# inheritance events

我在这里为其他程序员编写了几个UserControl。

一些与基本名称相同的暴露事件处理程序被接受

(UserControl CodesCombo -> SelectedValueChanged)

有些不是

(UserControl TextBox -> TextChanged)

我正在编写一个包含文本框的UserControl。我需要向该控件的潜在使用者公开TextChanged事件。

我有一个基于ComboBox的类似控件,并且我使用了

public event EventHandler SelectedValueChanged 
    {
    add { cbMain.SelectedValueChanged += value; }
    remove { cbMain.SelectedValueChanged -= value; }
    }         

公开“更改”事件“ SelectedValueChanged”,它的工作没有任何问题。

但是,当我尝试以类似方式在基于TextBox的控件中使用此技术时

public event EventHandler TextChanged
    {
    add { tbMain.TextChanged += value; }
    remove { tbMain.TextChanged -= value; }
    }

我收到警告消息:

'MyTextBox.TextChanged' hides inherited member 'UserControl.TextChanged'. Use new keyword if hiding was intentional.

除了显而易见的内容外,我不确定消息的含义,但是我知道的是我不-想想-我想隐藏任何东西。我有内部的SelectedValueChanged和TextChanged函数(cbMain_SelectedValueChanged,tbMain_TextChanged),这些函数可以满足我的一些需要,但我想允许消费者也可以对文本更改进行事件调用,就像在基于ComboBox的事件中一样。

此外,在测试程序的可用事件列表中,我根本没有任何“更改”事件。

我现在通过将事件公开为“ TextChange”来解决了这个问题

new public event EventHandler TextChange
    {
    add { tbMain.TextChanged += value; }
    remove { tbMain.TextChanged -= value; }
    }

这给了我列表中的一个事件,并且看起来似乎运行良好,但是我希望对此有一个通用的解决方案,因为我们正在为这些程序包制作更多的控件,我不认为我可以不用名字“ off”。

您知道此消息实际上是在告诉我什么,以及如何在内部获取我的活动,同时仍然让用户获得他们的信息吗?

谢谢!

更新:要求提供更具体的代码:

namespace NCFLSToolbox
    {
    public partial class NCFLSCodesCombo : UserControl
        {
        //Listed in the Events for System.Windows.Forms.ComboBox
        private void cbMain_SelectedValueChanged(object sender, EventArgs e)
            {
            ControlRequiredColoring();
            }

        //Exposed Event for user
        public event EventHandler SelectedValueChanged 
            {
            add { cbMain.SelectedValueChanged += value; }
            remove { cbMain.SelectedValueChanged -= value; }
            }
        }


    public partial class NCFLSTextbox : UserControl
        {
        //Listed in the Events for System.Windows.Forms.TextBox
        private void tbMain_TextChanged(object sender, EventArgs e)
            {
            ControlRequiredColoring();
            }


        //Couldn't expose "TextChanged" by name...
        ////public event EventHandler TextChanged
        ////    {
        ////    add { tbMain.TextChanged += value; }
        ////    remove { tbMain.TextChanged -= value; }
        ////    }

        //...so I exposed "TextChange" instead.
        public event EventHandler TextChange
            {
            add { tbMain.TextChanged += value; }
            remove { tbMain.TextChanged -= value; }
            }
        }
    }

2 个答案:

答案 0 :(得分:5)

  

您知道这条消息真正告诉我什么吗?

首先,了解隐藏的含义。暂时忘记这是一个事件。如果我们有:

class B     { public void M() { Console.WriteLine("B.M()"); } }
class D : B { public void M() { Console.WriteLine("D.M()"); } }

然后我们有两个方法都称为M。如果您说:

D d = new D();
B b = d;
d.M(); // D.M();
b.M(); // B.M();

编译器警告您有两种方法,而不是一种,而哪种方法取决于接收方的编译时类型, NOT 接收方的运行时类型。

为什么可能不是您想要的?

三个原因。

首先,许多C#程序员从Java进入C#,在Java中,方法被自动覆盖。在C#中,必须重写virtual方法。编译器告诉您您没有获得预期的语义。

第二,这是为了缓解脆性基类问题。假设B的作者为D团队提供了一个包含B且没有M的DLL程序集,D派生了D,并添加了方法D.M。然后B团队意识到他们可以实现M,因此他们将其添加到B并为D团队提供了一个新的程序集。现在应该警告D团队B.M存在,以便他们可以决定是否删除D.M。

第三,为什么首先要这样做?如果基类已经具有所需的事件,请使用它!不要用旧的来做新的。只需使用旧的。编译器告诉您,您肯定在做奇怪的事情,并且可能是错误的。

如果打算在两个不同的类上有两个成员,而一个成员隐藏另一个成员,则可以通过在该成员上放置new来告诉编译器“我已经考虑过这一点,这是有意的”。那就是“这是一个新成员,我的意思是使其成为新成员,而不是替代旧成员。”

答案 1 :(得分:1)

问题在于TextBox事件不会传播到父用户控件。如果您希望他们这样做,有两种方法可以实现:

1)让用户控件上的事件将事件处理程序的所有附加/删除操作转移到UserControl的事件处理程序中。这大致就是您的问题正在尝试做的事情。但是,我不建议这样做。

2)在父用户控件上的TextBox触发事件上触发事件。

因此,只需在InitializeComponent()下的构造函数中运行以下代码:
tbMain.TextChanged += (sender,e)=>OnTextChanged(e);

使用这种方法,TextBox中的TextChanged事件将调用OnTextChanged,这将引发一个TextChanged事件。您不能直接调用基类事件(因此提供了OnTextChanged方法的原因)。

编辑: tbMain.TextChanged += (sender,e)=>OnTextChanged(e);在语义上等效于以下代码:

tbMain.TextChanged += OnTbMainTextChanged;
...
} //End of Constructor

private void OnTbMainTextChanged(object sender, EventArgs e)
{
    OnTextChanged(e);
}

使用lambda函数的好处是它更加独立。除其他优点外,使用独立的lambda函数对于将来的代码维护人员来说很明显sender没有被传播,而无需维护人员导航到指定的方法。这就是您想要的:从订阅者到用户控件的角度来看,使用TextBox来实现控件是一种实现细节。