2011-07-26 13 views
17

Cuando intento compilar lo siguiente:¿Por qué un delegado de .NET no se declara estático?

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

recibo, como un error: "El modificador 'estático' no es válido para el presente artículo."

Estoy implementando esto en un singleton, con una clase separada que llama al delegado. El problema es que cuando uso la instancia singleton dentro de la otra clase para llamar al delegado (del identificador, no del tipo), no puedo hacer eso por la razón que sea, incluso cuando declaro que el delegado no es estático. Obviamente, solo puedo referirme a él a través del tipo directamente si y solo si el delegado es estático.

¿Cuál es el razonamiento detrás de esto? Estoy usando MonoDevelop 2.4.2.

actualización

Después de probar una de las sugerencias con el siguiente código:

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

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

He recibido un error de procesamiento, que establece que el MoveMethod debe ser un tipo, y no una identificador.

+0

Creo que un pequeño ejemplo de código te ayudará a explicar el problema. Leí el segundo párrafo cinco veces y todavía no tengo ni idea de qué y cómo quieres lograr. –

+0

¿Cuál es el propósito de la variable 'mm' en el método' ProcessMove'? Si se asigna un delegado (ya sea estático o de instancia) a 'moveDelegate', entonces la llamada' moveDelegate' llamará al delegado asignado, como debería. – Groo

+0

Creo que las interfaces son mejores para esto. Por favor, consulte [mi respuesta] (http://stackoverflow.com/questions/6835766/c-delegate-cannot-be-declared-static/6835948#6835948) –

Respuesta

26

Prueba esto:

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

Así el método de variables se pueden definir estática. La palabra clave static no tiene ningún significado para la definición delegate, al igual que las definiciones enum o const.

Un ejemplo de cómo asignar el método de campo estático:

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()); 
    } 
} 
+0

No estoy del todo siguiendo. Se supone que es un campo público, o un método? Si es un campo, ¿se supone que se refiere a una instancia de un delegado? Cuando trato de referenciarlo dentro de mi otra clase, obtengo un error: /. – zeboidlund

+0

Agregué un ejemplo. Como puede ver, la variable de método estático funciona como un campo estático. –

+0

La frase "al igual que enum o const definitions" no suena correcta. const es de naturaleza estática por defecto, por lo que no podemos tener estática para los campos de tipo const. El ejemplo dado es bueno. – Dhananjay

7

Usted está declarando un tipo delegate. No tiene sentido declararlo como static. Sin embargo, puede declarar una instancia de su tipo delegate como static.

public delegate void BoringDelegate(); 


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

Podría tener "sentido" y, de hecho, ser bastante útil para ser capaz de declarar un 'delegado' que nunca puede representar un método de instancia, es decir, que no sería un' '__thiscall' administrado, sino más bien un método administrado necesariamente estático (en una clase estática o de instancia). Tal tipo de "delegado" no tendría la propiedad 'Delegate.Target', y sus instancias no tendrían ninguna provisión para almacenar un objeto' this' ... ¿tal vez una clase base para el tipo 'Delegate' existente? La ventaja sería emitir 'call' en lugar de' callvirt' en el IL, lo que también podría evitar el apilamiento de un valor 'this' nulo. –

0

defina su delegado, en su clase estática declare una variable de instancia para él.

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

public static MyClass 
{ 
    public static MoveDelegate MoveDelegateInstance; 
} 
2

declaración de delegado es en realidad una declaración de tipo. No puede ser estático, al igual que no puede definir una enumeración o estructura estática.

Sin embargo, prefiero usar una interfaz en lugar de raw delegate.

Considere esto:

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

En su código de llamada:

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 

Este similares en espíritu a Strategy design pattern y le permite desacoplar Actor movimiento de la estrategia de implementación real (consola, gráfico, cualquiera que sea) Es posible que luego se requieran otros métodos de estrategia que lo hagan una mejor opción que un delegado.

Finalmente, puede usar un Inversion of Control framework para inyectar automáticamente la instancia de estrategia correcta en sus clases Actor, por lo que no es necesario inicializar manualmente.

+0

Estoy intentando escribir un motor de juego basado en texto que me permite crear objetos y moverlos por el mapa, mostrando las coordenadas de los objetos a la consola. Esto debe hacerse antes de agregar cualquier GUI, gráficos, etc. Estoy en el punto donde decidí crear una clase de teletransportador, que hice un singleton, para mover cualquier objeto a un destino específico. Por ahora lo mantengo simple: MoveDirection.Forward, o MoveDirection.Backward. Si quieres que publique una fuente, házmelo saber. – zeboidlund

+0

@Holland: Prefiero ir con interfaces (si tengo el problema correcto). Por favor mira mi edición. –

5

Una declaración de delegado básicamente declara un firma de método, que solo incluye información sobre sus parámetros y el tipo de devolución. Y dado que el mismo delegado puede apuntar a métodos estáticos y de instancia, no tiene sentido hacer que la firma del método sea estática o instancia.

Una vez que haya declarado su delegado como:

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

que significa que cualquier delegado de este tipo debe apuntar a un método que acepta uno Actor parámetro, uno MoveDirection parámetro y devuelve void, independientemente de si el método es estático o instancia. Puede declarar el delegado en el ámbito del espacio de nombres o dentro de una clase (del mismo modo que declararía una clase anidada).

Así que después de declarar la MoveDelegate algún lugar, puede la creación de campos y variables de ese tipo:

private MoveDelegate _myMoveDelegate; 

y recordar que el método debe tener una firma juego:

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

entonces puede asignar este método a un delegado en otro lugar:

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

Es útil saber que .NET (a partir de la versión v3.5) proporciona algunos predefinidos delegados genéricos (Action y Func) que pueden ser utilizados en lugar de declarar sus propios delegados:

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

El uso de estos delegados es en mi humilde opinión más legible, ya que puede identificar inmediatamente la firma de los parámetros mirando al propio delegado (mientras que en su caso es necesario buscar la declaración).

+0

Sus comentarios sobre la firma de un delegado son correctos, pero no están relacionados con la cuestión de si la instancia delegada * representa un método estático frente a instancia * *. Eso * podría * ser lo que el OP estaba buscando ... al menos así fue como terminé en esta página. Quizás su respuesta podría hacer mención del hecho de que dos instancias del mismo tipo de delegado pueden representar métodos con aparentemente la misma firma, pero donde uno es estático y la otra instancia. Es una diferencia significativa que debe ser entendida, a pesar de los esfuerzos de C# por ocultarla. Ver mi otro comentario para motivaciones adicionales. –

+0

Acabo de encontrar una discusión sobre la relación (profana, en mi humilde opinión) entre delegados estáticos y de instancia en https://msdn.microsoft.com/en-us/library/z84bd3st(v=vs.110).aspx. Hay un ejemplo de código particularmente bueno en la parte inferior. –

+0

@Glenn: tal vez debería actualizar la respuesta, pero la verdad es que los delegados en .NET pueden apuntar a métodos estáticos y de instancia, y almacena la información sobre el destino internamente. De hecho, hay varias maneras diferentes de almacenar esto, como se explica [aquí] (http://mattwarren.org/2017/01/25/How-do-.NET-delegates-work/) en "Diferentes tipos de delegados ". El tipo interno del delegado determinará cómo el JITter organizará los parámetros en la pila para que coincidan con la firma del objetivo real, al tiempo que permite que el código de llamada transmita parámetros de forma transparente al delegado. – Groo

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

Déjeme decirle lo que sucedió cuando se declaró un delegado

El compilador crea una clase, en este caso llamado MoveDelegate, y se extiende con System.MulticastDelegate.

Dado que no se puede extender ningún tipo no estático por tipo estático.

Este es el motivo por el que el compilador no permite la declaración de delegado estática. Pero aún puede tener una referencia de delegado estática.

Cuestiones relacionadas