2014-04-08 16 views
10

In Unity müssen Sie bei Verwendung von Coroutinen oder InvokeRepeating eine Zeichenfolge mit dem Namen der Funktion angeben, die Sie aufrufen möchten. Dies ist jedoch ein Problem, wenn Sie den Namen dieser Funktion ändern, da Sie daran denken müssen, die verwendeten Routinen zu ändern. Gibt es einen saubereren Weg, dies zu tun?Name der Funktion in C#

Derzeit sieht es wie folgt aus:

InvokeRepeating ("SendChangedValues", SEND_RATE, SEND_RATE); 

obwohl es schön wäre,

etwas wie
InvokeRepeating (SendChangedValues.Name(), SEND_RATE, SEND_RATE); //or 
InvokeRepeating (functions.GetName(SendChangedValues), SEND_RATE, SEND_RATE); 

zu haben Ist das möglich in C#? Oder etwas anderes, das sicherstellt, dass ich einen Fehler/eine Warnung erhalte, wenn ich den Namen der Funktion ändere, ohne diese Zeichenfolgen zu ändern.

Edit 1: Die sauberste Sache, die ich mir vorstellen konnte, ist, eine const-Zeichenkette mit dem Namen der Funktion zu machen und sie direkt vor die Funktion selbst zu setzen. Es ist also schwieriger zu vergessen, die Zeichenkette zu ändern, da sie direkt darüber liegt, und ich muss nur diese eine konstante Zeichenkette ändern, um alle Koroutinen zu ändern.

Danke!

+2

Ich habe noch nie davon gehört, aber ID schlägt vor, Sie erstellen eine Zeichenfolge Variable zu verwenden, wenn Sie es in einem Aufruf verwenden müssen und diese Zeichenfolge neben der entsprechenden Methode – Sayse

+0

Haha, ja, das ist auch, was ich gerade dachte: DI hat meine Frage bearbeitet, während Sie Ihren Kommentar geschrieben haben: P –

+0

Sie können wahrscheinlich ein benutzerdefiniertes Attribut erstellen, um Ihre Funktionen zu dekorieren, und dieses Attribut kann eine Zeichenfolge als Bezeichner verwenden, die sich nicht ändern muss, wenn sich der Funktionsname ändert. Dann muss ein Code, der diese Zeichenfolge enthält, eine Funktion finden, die mit einem mit dieser Zeichenfolge initialisierten benutzerdefinierten Attribut versehen ist, den Methodennamen abrufen und als Parameter an "InvokeRepeating" übergeben.Es kann nicht garantiert werden, dass jemand die Zeichenfolge des Attributs ändert, aber das sollte nicht nötig sein und somit die Wahrscheinlichkeit eines schlechten Code-Refactors (theoretisch zumindest) verringern. – LB2

Antwort

9

ahhh .. wenn es als nächstes die C# Version wäre, hätten Sie den Namen von Operator verwenden können.

für jetzt, hilft das Ihrer Ursache?

private static string GetFunctionName(Action method) 
{ 
return method.Method.Name; 
} 

namens:

string methodName = GetFunctionName(SendChangedValues); 

Sie vielleicht verschiedene Delegattypen erkunden .. Aktion, Func usw.

Das einzige Problem mit obigem ist, dass Sie für jede Methodensignatur möglicherweise die richtige Signatur/Überladung definieren müssen, um den Namen zu erhalten.

+0

Das funktioniert super. Obwohl für Funktionen mit Parametern habe ich 'String GetName (Aktion Methode)' und das ist in Ordnung. Aber wenn ich das anrufe, muss ich auch den Typ der Variablen hinzufügen: 'Helpers.GetName (MethodWithStringParam)'. Ich habe mich gefragt, ob es da einen Weg gibt, also kann ich 'Helpers.GetName (MethodWithStringParam)' einfach machen. Es ist kein Angebot, wäre aber nett. Vielen Dank! –

2

Ich verstehe Ihr Problem, und ich habe keine goldene Lösung dafür.

Was könnte helfen, Lambda-Ausdrücke als eine Möglichkeit, um es herauszufinden, wie in diesem blog post.

Es beschreibt eine Möglichkeit, den Methodennamen herauszufinden, indem eine Expression übergeben wird, die als MethodCallExpression ausgewertet wird. Von dort können Sie die relevanten Informationen extrahieren.

2

Ich erinnere mich, etwas ähnliches in einem anderen Kontext zu tun. Es ging ungefähr so:

Sie eine Methode der eigenen erklären, dass die ursprüngliche Methode verwendet:

public static void MyInvokeRepeating<T>(Func<T> method) 
{ 
    InvokeRepeating(method.Method.Name, SEND_RATE, SEND_RATE); 
} 

und rufen Sie dann Ihre Methode wie diese

MyInvokeRepeating(someObject.SomeFunction); 

Noch besser wäre es, machen ein Erweiterungsmethode.

1

Kein Lambda-Ausdruck erforderlich, wenn Sie mit einer Methode arbeiten (keine Eigenschaft oder kein Feld).

Sie können einen InvokeRepeating Wrapper schreiben, der einen Delegaten nimmt. Rufen Sie GetInvocationList auf dem Delegaten auf, um die MethodInfo zu erhalten, die den Namen hat.

3

Coroutines erlauben es IF übergeben Sie keinen zusätzlichen Parameter. Das erste Beispiel in the documentation zeigt eine Überlastung mit der Unterschrift von Coroutine StartCoroutine(IEnumerator routine);:

using UnityEngine; 
using System.Collections; 

public class Example : MonoBehaviour { 
    void Start() { 
     print("Starting " + Time.time); 
     StartCoroutine(WaitAndPrint(2.0F)); 
     print("Before WaitAndPrint Finishes " + Time.time); 
    } 
    IEnumerator WaitAndPrint(float waitTime) { 
     yield return new WaitForSeconds(waitTime); 
     print("WaitAndPrint " + Time.time); 
    } 
} 

InvokeRepeating, ist leider nicht die gleiche Überlastung hat. Sie sollten es in der Lage sein, es dort durch Reflexion zu erreichen, wie in einigen der anderen Antworten erwähnt.

2

Sie können dies über statische Reflexion (in mindestens .NET 4, vielleicht 3,5 und unten)

// This 
InvokeRepeating(((MethodCallExpression)((Expression<Action<string>>)(x => x.ToString())).Body).Method.Name, SEND_RATE, SEND_RATE); 

// Is the same as 
InvokeRepeating("ToString", SEND_RATE, SEND_RATE); 

Sie müssen nur die Signatur der Funktion kennen Sie anrufen und ersetzen Sie die Aktion im obigen Beispiel entsprechend.

+0

Das ist waaaaay komplizierter als nötig. –

+0

@BenVoigt Ich bin mir nicht sicher, ob ich zustimme, dass es komplizierter ist, als einen Wrapper für die InvokeRepeating-Funktion zu schreiben, die an anderer Stelle gepflegt wird. Es ist auch genau das, was von einigen Kommentaren (http://joelabrahamsson.com/getting-property-and-method-names-using-static-reflection-in-c/) vorgeschlagen wurde, ohne es in eine Utility-Klasse zu integrieren. – Michael

+0

Vergleichen Sie mit pmcoltranes Kommentar ... –

4

Ich benutze die folgenden in meiner Codebasis, extrapolieren für invokerepeating.

public delegate void Task(); 

public static class MonoBehaviourExtensions { 
    public static void InvokeLater(
    this MonoBehaviour b, 
    float time, 
    Task task) 
    { 
    b.Invoke(task.Method.Name, time); 
    } 
} 

Verwenden Sie es wie dieses

public class MyBehaviour : MonoBehaviour { 
    void Start(){ 
    this.InvokeLater(1f, DoSomething1SecondLater); 
    } 

    public void DoSomething1SecondLater(){ 
    Debug.Log("Welcome to the future"); 
    } 
} 
2

eine enum machen den Koroutinen aus Sie haben:

public enum MyCoroutines 
{ 
Coroutine1, 
Coroutine2 
} 

InvokeRepeating(MyCoroutines.Coroutine1.ToString(), x, y); 

Auf diese Weise können die Funktionsnamen keine Gefahr falscher Schreibweise haben plus Sie können finden und ersetze sie leicht. Es ist auch sehr gut zum Abspielen von Sounds und Animationen.

0

Hier ist meine Sicht auf die Angelegenheit, seit ich vor kurzem auf dieses Problem im Zusammenhang von UnityEvent Listener stolperte, die auch gespeichert sind und nur über den Namen der Methode abgerufen werden können. Mein Ansatz macht mehr Sinn im Umgang mit Event-Handler mit verschiedenen Signaturen, aber es funktioniert auch für alle Aktion und Invoke auch.

public void Start() 
{ 
    Invoke(
     methodName : GetMethodName((System.Action)Explode), 
     time: 1f); 

    Invoke(
     methodName : GetMethodName((System.Action<int>)HandleMyNumber), 
     time: 2f); 
} 

public static string GetMethodName(System.Delegate @delegate) 
{ 
    return @delegate.Method.Name; 
} 

private void Explode() 
{ 
    // Do something Action like... 
} 

private void HandleMyNumber(int number) 
{ 
    // Do something Action<int> like... 
} 

Wie andere bereits erwähnt haben, ist die einfachste Möglichkeit, die Method des Abrufens eine Methode Namen zu erhalten, ist über einen Delegattyp wie Aktion. Ich möchte keine Überladungen für jeden Delegattyp wie Aktion, Aktion, Aktion angeben, da ich diese Parameter nicht verwende. Stattdessen kann ich einfach jede Methode, die ich als Parameter übergeben würde, an den richtigen Typ übergeben.