【问题标题】:How to call non static methods from Action<T> delegate in C#如何在 C# 中从 Action<T> 委托调用非静态方法
【发布时间】:2014-12-12 06:19:01
【问题描述】:

由于我正在为要执行的某些操作编写一个通用概念,因此我需要在操作委托中调用一些非静态方法。此外,它们在我的代码中都不是静态的。但是我仍然不能在 Action 定义中调用非静态方法。 这是我的代码-

private Dictionary<string, Action<object>> m_dicUndoRedoAction = new Dictionary<string, Action<object>>();
m_dicUndoRedoAction.Add("DeleteClass", DeleteClassFromeNode );

这里是 DeleteClass 的定义

private Action<object> DeleteClassFromeNode =
  data =>
  {
    Tuple<itemType1, itemType2> items = data as Tuple<itemType1, itemType2>;
    if (items != null && items.Item2 != null)
    {
      DeleteClass(items.Item2); // This is my non static method in the same class.
    }
  };

这是我如何称呼委托人

private void Undo_Executed(object sender, ExecutedRoutedEventArgs e)
{
  object temp;
  if (UndoRedoAction.DoUndo(out temp))
  {
    m_dicUndoRedoAction["DeleteClass"].Invoke(temp);
  }
}

正如编译器所说的

字段初始值设定项不能引用非静态字段、方法或 属性'DeleteClassFromeNode'

我还查看了 Action 文章的 MSDN 参考资料,但 MS 没有提到,Action 是隐式静态的,还是我走错了路? 我还查看了来自静态方法的一些非静态调用,但没有一个得到令人满意的解释。 如果有人提供它的低级解释,我将不胜感激。

回应彼得的解释

虽然初始化程序在构造函数完成之前运行,但这并不能在构造函数执行之间触发委托。即使您将在 ILDASM 中查找其汇编代码,它也会将实际操作字段显示为非静态,但将缓存的匿名委托对象显示为静态。为什么编译器会出现这种不同的行为?

【问题讨论】:

  • private Action&lt;object&gt; DeleteClassFromeNode = ... 似乎有些奇怪。为什么你不能简单地把它变成一个方法,比如private void DeleteClassFromeNode(object data) { ... }m_dicUndoRedoAction.Add("DeleteClass", DeleteClassFromeNode ); 仍然会编译,如果你担心的话。
  • 嗨 hvd,因为我通常在我的代码中使用匿名字段。显然这不违反 csc 规则。
  • 您的代码违反了 C# 语言规则。它不是很明显,但是为了初始化 lambda,编译器需要使用 this。它被捕获,是必需的,因为 DeleteClass() 是一个实例方法。而且你不能使用 this 来初始化字段,在字段被初始化时构造函数还没有完成运行。您必须将初始化移动到构造函数中。可能不是您喜欢的“风格”,但很有必要。
  • @hvd 请把它写成答案。这显然是最好的解决方案。致 Rohit Prakash:箭头=&gt; 仍将被编译器转换为方法。在您的情况下,最好为该方法使用名称 DeleteClassFromeNode。当然,命名方法(非静态)的主体可以引用this 的其他实例成员。正如 hvd 所说,“方法组”DeleteClassFromeNode 隐式转换为类型Action&lt;object&gt;。也许方法参数data 的类型应该是一些Tuple&lt;,&gt; 而不仅仅是object?请注意,Action&lt;in T&gt;T 中是逆变
  • @JeppeStigNielsen 谢谢,但如果它不能回答问题,我会尽量不发布答案。 Peter Duniho 的回答对于所提出的问题来说是正确的,即使我怀疑所提出的问题不是应该提出的问题。

标签: c# generics static delegates


【解决方案1】:

正如编译器告诉你的,你被禁止在初始化器中使用非静态成员。这是因为初始化程序在构造函数完成之前运行,因此使用非静态成员不一定安全。

相反,只需在构造函数中执行初始化:

public MyClass()
{
    DeleteClassFromeNode = data =>
    {
        Tuple<itemType1, itemType2> items = data as Tuple<itemType1, itemType2>;
        if (items != null && items.Item2 != null)
        {
          DeleteClass(items.Item2); // This is my non static method in the same class.
        }
    };

    // Other initialization code can go here (or before...whatever is most appropriate)
}

【讨论】:

  • 虽然这可行,但最好简单地将DeleteClassFromeNode 设为MyClass 上的普通(命名,非匿名)实例方法。所以void DeleteClassFromeNode(object data) { ... }。当然,在该方法的主体内,您可以调用非静态成员DeleteClass。有关更多信息,请参阅问题下的 hvd 评论。将其作为委托字段的唯一原因是,如果您打算稍后使用 =+=-= 重新分配该字段。你是?如果不是,换成普通方法(或者至少声明字段readonly)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多