将事件处理程序作为方法参数传递

3

我想要将事件处理程序传递给我的某个方法。这可能看起来很愚蠢,但我似乎找不到方法。

我在SO上看了一些相关问题,但还是没有解决。

我有以下的方法签名

public void SyncWithInfo(Action<object, EventArgs> cellValueChanged,
    int indexValeurFr, int indexValeurEn, int indexUnite)

我正在试图使用以下这段代码来调用它:

private void myMethod()
{
    dgvform.SyncWithInfo(dgvform_CellValueChanged, valeur.Index,
        valeurEN.Index, unite.Index);
}

private void dgvform_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    //stuff
}

我遇到了以下错误:
无法将“方法组”转换为“System.Action”
根据我在这里阅读到的内容,如果直接按照名称引用某个方法而没有任何参数,则被视为“方法组”,因为该方法可能有多个重载。
那么我的问题是:如何将我的事件处理程序作为参数传递给我的方法?

你的DataGridViewCellEventargs和委托事件参数之间存在不匹配。通常来说,向上转换(upcasting)是可行的,但这里很可能存在一个方差问题。 - BradleyDotNET
@LordTakkera 哦我的天啊,就是这样...我简直不敢相信它是那么愚蠢。 - Rémi
你遇到的第一个编译器错误是方法的最佳重载匹配具有一些无效参数,这是准确的。你提到的第二个错误是误导性的。 - Olivier Jacot-Descombes
@OlivierJacot-Descombes 我同意,但我仍然不明白为什么DataGridViewCellEventArgs是一个EventArgs,但它还是不行。 - Rémi
请参见我的答案以获取解释。 - BradleyDotNET
3个回答

4

我认为SyncWithInfo方法的签名应该使用EventArg的显式类型,这样方法签名看起来像:

public void SyncWithInfo(Action<object, DataGridViewCellEventArgs> cellValueChanged,
    int indexValeurFr, int indexValeurEn, int indexUnite)

这解决了我的编译错误,所以我会将其标记为答案。 - Rémi

3
接受的答案给出了解决方案,但为了尽量提供一些解释:
通常情况下,您可以将派生类型分配给其基类型:
EventArgs e = new DataGridViewCellEventArgs();

然而,当你开始处理泛型(这就是你的操作,一个泛型委托)时,事情会变得有点奇怪。你必须开始处理一个叫做“协变”的概念。默认情况下,泛型不是协变的。协变意味着你可以使用派生类型作为类型参数。逆变意味着你可以反过来使用。
一些泛型,比如IEnumerable,被标记为协变(通过使用out T作为它们的类型参数)。这就是为什么你可以这样做的原因:
IEnumerable<object> = new List<String>();

动作委托不是协变的,因此示例代码将无法编译。

MSDN 上的协变和逆变


谢谢您提供更多关于它未能正常工作原因的信息。给您一个+1。 - Rémi
这与此处的通用类型参数的差异无关。即使使用相应的非泛型委托声明参数,问题仍会发生。请参见我的答案。 - Olivier Jacot-Descombes

3
@LordTakkera的解释是错误的(抱歉)。这与泛型相关的方差无关。您将在普通(非泛型)委托中遇到同样的问题。
一个方法可以接受更具体类型的值,而不仅仅是在方法头中指定的参数类型。我们需要考虑的方法是作为参数传递给另一个方法的事件处理程序。如果此事件处理程序期望类型为DataGridViewCellEventArgs的参数,则不会接受比它更不具体的EventArgs类型的参数。
如果您可以将此事件处理程序作为代表传递给另一个方法的Action<object,EventArgs>类型的参数,则此其他方法可以调用我们的事件处理程序并向其传递EventArgs参数;然而,事件处理程序期望DataGridViewCellEventArgs参数。
另一方面,相反的情况是可行的。
private void AcceptsEventHandler(Action<object, DataGridViewCellEventArgs> handler)
{
    // handler is SomeEventHandler if we call:
    //           AcceptsEventHandler(SomeEventHandler);

    handler(this, new DataGridViewCellEventArgs(1, 2));
}

private void SomeEventHandler(object sender, EventArgs e)
{
   // We are getting a DataGridViewCellEventArgs as second argument here, which is ok.
}

这有效:
AcceptsEventHandler(SomeEventHandler);

+1 这很有道理...现在你指出来了,它变得非常清晰。 - Rémi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接