关于c#:为什么不能将.NET委托声明为静态?

Why can a .NET delegate not be declared static?

当我试图编译以下内容时:

1
public static delegate void MoveDelegate (Actor sender, MoveDirection args);

我收到一个错误消息:"修改器'static'对此项无效。"

我将在一个单独的类(调用委托)中实现这一点。问题是,当我使用另一个类中的singleton实例调用委托(从标识符,而不是类型)时,无论出于什么原因,我都不能这样做,即使我声明委托是非静态的。显然,我只能在委托是静态的情况下通过类型直接引用它。

这背后的原因是什么?我正在使用MonoDevelop2.4.2。

更新

使用以下代码尝试其中一个建议后:

1
2
3
4
5
6
7
8
9
10
public void Move(MoveDirection moveDir)
{
    ProcessMove(moveDir);
}

public void ProcessMove(MoveDirection moveDir)
{
    Teleporter.MoveMethod mm = new Teleporter.MoveMethod(Move);
    moveDelegate(this, moveDir);
}

我收到一个处理错误,它指出movemethod必须是一个类型,而不是标识符。


试试这个:

1
2
public delegate void MoveDelegate(object o);
public static MoveDelegate MoveMethod;

所以方法变量可以定义为静态的。关键字staticdelegate定义没有意义,就像enumconst定义一样。

如何分配静态方法字段的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class A
{
  public delegate void MoveDelegate(object o);
  public static MoveDelegate MoveMethod;
}

public class B
{
  public static void MoveIt(object o)
  {
    // Do something
  }    
}

public class C
{
  public void Assign()
  {
    A.MoveMethod = B.MoveIt;
  }

  public void DoSomething()
  {
    if (A.MoveMethod!=null)
      A.MoveMethod(new object());
  }
}


您正在声明一个delegate类型。把它声明为static是没有意义的。不过,您可以将您的delegate类型的实例声明为static

1
2
3
4
5
6
7
8
9
public delegate void BoringDelegate();


internal class Bar {
    public static BoringDelegate NoOp;
    static Bar() {
        NoOp = () => { };
    }
}


委托声明基本上声明了一个方法签名,它只包含有关其参数和返回类型的信息。由于同一个委托可以同时指向静态方法和实例方法,因此将方法签名本身设置为静态或实例是没有意义的。

一旦你宣布你的代表为:

1
public delegate void MoveDelegate (Actor sender, MoveDirection args);

这意味着任何此类委托都必须指向接受一个Actor参数、一个MoveDirection参数并返回void的方法,而不管该方法是静态的还是实例的。可以在命名空间范围或类内部声明委托(就像声明嵌套类一样)。

因此,在某处声明MoveDelegate之后,您可以创建该类型的字段和变量:

1
private MoveDelegate _myMoveDelegate;

请记住,该方法应具有匹配的签名:

1
2
3
4
5
6
7
8
9
10
// parameters and return type must match!
public void Move(Actor actor, MoveDirection moveDir)
{
    ProcessMove (moveDir);
}

public static void MoveStatic(Actor actor, MoveDirection moveDir)
{
    ProcessMove (moveDir);
}

然后,可以将此方法分配给其他位置的委托:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void SomeOtherMethod()
{
     // get a reference to the Move method
     _myMoveDelegate = Move;

     // or, alternatively the longer version:
     // _myMoveDelegate = new MoveDelegate(Move);

     // works for static methods too
     _myMoveDelegate = MoveStatic;

     // and then simply call the Move method indirectly
     _myMoveDelegate(someActor, someDirection);
}

知道.NET(从v3.5版开始)提供了一些预定义的通用委托(ActionFunc可以使用,而不是声明自己的委托:

1
2
3
// you can simply use the Action delegate to declare the
// method which accepts these same parameters
private Action<Actor, MoveDirection> _myMoveDelegate;

使用这些委托更易读,因为您可以通过查看委托本身立即识别参数的签名(而在您的情况下,需要查找声明)。


委托声明实际上是一个类型声明。它不能是静态的,就像不能定义静态枚举或结构一样。

但是,我宁愿使用接口而不是原始委托。

考虑一下:

4

在您的呼叫代码中:

1
2
3
4
5
var strategy = new ConsoleGameStrategy();

// when creating Actors, specify the strategy you want to use
var actor = new Actor(strategy) { Name ="Forrest Gump" };
actor.RunForrestRun(); // will write to console

这在精神上类似于策略设计模式,并允许您将Actor的移动与实际的实现策略(控制台、图形、任何东西)分离开来。之后可能需要其他策略方法,这使得它比代表更好地选择。

最后,您可以使用控制框架的反转来自动在您的Actor类中注入正确的策略实例,因此不需要手动初始化。


定义委托,在静态类中为其声明一个实例变量。

1
2
3
4
5
6
public delegate void MoveDelegate (Actor sender, MoveDirection args);

public static MyClass
{
     public static MoveDelegate MoveDelegateInstance;
}

1
public static delegate void MoveDelegate (Actor sender, MoveDirection args);

我来告诉你当你宣布代表时发生了什么

编译器创建一个名为MoveDelegate的类,并用System.MulticastDelegate扩展它。

因为不能按静态类型扩展任何非静态类型。

所以这就是编译器不允许静态委托声明的原因。但仍然可以有静态委托引用。