Почему делегат .NET не может быть объявлен статическим? - PullRequest
23 голосов
/ 26 июля 2011

Когда я пытаюсь скомпилировать следующее:

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

Я получаю сообщение об ошибке: «Модификатор« static »недопустим для этого элемента».

Я реализую это в одиночном, с отдельным классом, который вызывает делегат. Проблема в том, что когда я использую экземпляр singleton внутри другого класса для вызова делегата (из идентификатора, а не типа), я не могу этого сделать по любой причине, даже когда я объявляю делегат нестатическим. Очевидно, что я могу ссылаться на него через тип напрямую только тогда и только тогда, когда делегат является статическим.

В чем причина этого? Я использую MonoDevelop 2.4.2.

обновление

Попробовав одно из предложений со следующим кодом:

public void Move(MoveDirection moveDir)
{
    ProcessMove(moveDir);
}

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

Я получил ошибку обработки, в которой говорится, что MoveMethod должен быть типом, а не идентификатором.

Ответы [ 6 ]

32 голосов
/ 26 июля 2011

Попробуйте это:

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

Таким образом, переменная метода может быть определена как статическая. Ключевое слово static не имеет значения для определения delegate, как и определения enum или const.

Пример того, как назначить поле статического метода:

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()); 
  }
}
8 голосов
/ 26 июля 2011

Вы объявляете тип delegate.Нет смысла объявлять его как static.Вы можете объявить экземпляр вашего delegate типа как static.

public delegate void BoringDelegate();


internal class Bar {
    public static BoringDelegate NoOp;
    static Bar() {
        NoOp = () => { };
    }
}
5 голосов
/ 27 июля 2011

Объявление делегата в основном объявляет подпись метода , которая включает только информацию о его параметрах и типе возвращаемого значения.А поскольку один и тот же делегат может указывать как на статические, так и на методы экземпляров, не имеет смысла делать саму сигнатуру метода статической или экземпляром.

Как только вы объявили свой делегат как:

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

это означает, что любой делегат этого типа должен указывать на метод, который принимает один Actor параметр, один MoveDirection параметр и возвращает void, независимо от того, является ли метод статическим илипример.Вы можете объявить делегат в области пространства имен или внутри класса (точно так же, как вы бы объявили вложенный класс).

Таким образом, после объявления где-то MoveDelegate вы можете создавать поля и переменные этого типа:

private MoveDelegate _myMoveDelegate;

и помните, что метод должен иметь соответствующую подпись:

// 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);
}

, тогда вы можете назначить этот метод делегату в другом месте:

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) предоставляет несколько предопределенных универсальных делегатов (Action и Func), которые можно использовать вместо объявления ваших собственных делегатов :

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

Использование этих делегатов ИМХО более читабельно, поскольку вы можете сразу определить сигнатуру параметров, глядя на самого делегата (в то время как в вашем случае нужноищите декларацию).

2 голосов
/ 27 июля 2011

Объявление делегата на самом деле является объявлением типа. Он не может быть статическим, как вы не можете определить статическое перечисление или структуру.

Однако я бы предпочел использовать интерфейс вместо необработанного делегата .

Учтите это:

public interface IGameStrategy {
    void Move(Actor actor, MoveDirection direction);
}

public class ConsoleGameStrategy : IGameStrategy {
    public void Move(Actor actor, MoveDirection direction)
    {
        // basic console implementation
        Console.WriteLine("{0} moved {1}", actor.Name, direction);
    }
}

public class Actor {
    private IGameStrategy strategy; // hold a reference to strategy

    public string Name { get; set; }    

    public Actor(IGameStrategy strategy)
    {
        this.strategy = strategy;
    }

    public void RunForrestRun()
    {
        // whenever I want to move this actor, I may call strategy.Move() method

        for (int i = 0; i < 10; i++)
            strategy.Move(this, MoveDirection.Forward);
    }
}

В вашем телефонном коде:

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 движение от фактической стратегии реализации (консольной, графической и т. Д.). Позже могут потребоваться другие методы стратегии, что делает его лучшим выбором, чем делегат.

Наконец, вы можете использовать Inversion of Control framework для автоматического внедрения правильного экземпляра стратегии в ваши Actor классы, поэтому нет необходимости в ручной инициализации.

0 голосов
/ 20 сентября 2016
public static delegate void MoveDelegate (Actor sender, MoveDirection args);

Позвольте мне рассказать вам, что произошло, когда вы объявили делегат

Компилятор создает класс, в данном случае с именем MoveDelegate, и расширяет его с помощью System.MulticastDelegate.

* 1007.* Поскольку вы не можете расширить любой нестатический тип статическим типом.

Так что это причина, по которой компилятор не разрешает статическое объявление делегата.Но все же вы можете иметь статическую ссылку на делегат.

0 голосов
/ 26 июля 2011

определите ваш делегат, в вашем статическом классе объявите для него переменную экземпляра.

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

public static MyClass
{
     public static MoveDelegate MoveDelegateInstance;
}
...