2014-04-08 13 views
10

W Unity podczas używania coroutines lub InvokeRepeating, musisz podać ciąg znaków z nazwą funkcji, którą chcesz wywołać. Chociaż zmiana nazwy tej funkcji jest uciążliwa, ponieważ trzeba pamiętać o zmianie współprogramów, które jej używają. Czy jest to czystszy sposób na zrobienie tego?Uzyskaj nazwę funkcji w języku C#

Obecnie wygląda to tak:

InvokeRepeating ("SendChangedValues", SEND_RATE, SEND_RATE); 

choć byłoby miło mieć coś podobnego

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

Czy jest to możliwe w języku C#? Lub coś innego, co sprawia, że ​​dostaję błąd/ostrzeżenie, gdy zmieniam nazwę funkcji bez zmiany tych łańcuchów.

Edycja 1: Najczystsza rzecz jaką mogłem wymyślić to stworzenie ciągu znaków z nazwą funkcji i umieszczenie go tuż przed samą funkcją. Trudniej jest więc zapomnieć o zmianie ciągu znaków, ponieważ znajduje się on nad nim i muszę zmienić tylko jeden ciąg znaków, aby zmienić wszystkie współprogramy.

Dzięki!

+2

Nigdy nie słyszałem o tym, ale id sugeruje, aby utworzyć zmienną łańcuchową do użycia, gdy trzeba jej użyć w wywołaniu i zachować ten ciąg obok odpowiedniej metody. – Sayse

+0

Haha, tak, to także to, o czym właśnie pomyślałem: DI edytował moje pytanie podczas pisania twojego komentarza: P –

+0

Prawdopodobnie możesz stworzyć niestandardowy atrybut, aby udekorować twoje funkcje, a ten atrybut może wziąć ciąg znaków jako identyfikator, który nie musi się zmieniać, jeśli zmieni się nazwa funkcji. Następnie wpisz kod, który pod warunkiem, że ten ciąg znajdzie funkcję, która jest ozdobiona niestandardowym atrybutem zainicjowanym za pomocą tego ciągu, pobierz nazwę metody i przekaż ją jako parametr "InvokeRepeating".Nie gwarantuje to, że ktoś zmieni ciąg atrybutu, ale nie powinno się tego wymagać, a tym samym zmniejszyłby szanse na słaby refactor kodu (przynajmniej w teorii). – LB2

Odpowiedz

9

ahhh .. gdyby była następna wersja C#, mógłbyś użyć operatora nameof.

na teraz, czy to pomaga twojej sprawie?

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

nazywane przy użyciu:

string methodName = GetFunctionName(SendChangedValues); 

może chcesz poznać różne typy delegata .. Akcja, itp funkc

Jedynym problemem z powyższym jest to, że dla każdego podpisu metody, może być konieczne zdefiniowanie odpowiedniego podpisu/przeciążenia, aby uzyskać nazwę.

+0

Działa to świetnie. Chociaż dla funkcji z parametrami mam 'string GetName (Action method)' i to jest w porządku. Chociaż kiedy to nazywam, muszę również dodać typ zmiennej: 'Helpers.GetName (MethodWithStringParam)'. Zastanawiam się, czy istnieje sposób obejścia tego, więc mogę po prostu zrobić 'Helpers.GetName (MethodWithStringParam)'. To nie jest oferta, ale byłoby miło. Dzięki! –

2

Rozumiem twój problem i nie mam na to złotego rozwiązania.

Co może pomóc, używając wyrażeń lambda jako sposobu, aby to zrozumieć, jak w tym blog post.

Opisuje sposób, aby dowiedzieć się nazwę metody, przekazując Expression, które będą oceniane jako MethodCallExpression. Stamtąd możesz wyodrębnić odpowiednie informacje.

2

Pamiętam, że robiłem coś podobnego w innym kontekście. Trwało to mniej więcej tak:

zadeklarować metodę własną rękę, który wykorzystuje oryginalną metodę:

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

a następnie wezwać sposób jak ten

MyInvokeRepeating(someObject.SomeFunction); 

Jeszcze lepiej, zrób metoda rozszerzenia.

1

Żadne wyrażenie lambda nie jest potrzebne, gdy mamy do czynienia z metodą (nie właściwością lub polem).

Możesz napisać opakowanie InvokeRepeating, które przyjmuje delegata. Zadzwoń na GetInvocationList na delegata, aby uzyskać MethodInfo, który ma nazwę.

3

Coroutines pozwalają na to IF nie przekazujesz dodatkowego parametru. Pierwszy przykład pokazuje przeciążenie in the documentation z podpisem 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, niestety, nie ma tego samego przeciążenie. Powinieneś być w stanie to osiągnąć za pomocą refleksji, jak wspomniano w niektórych innych odpowiedziach.

2

Można to zrobić za pośrednictwem refleksji statycznej (przynajmniej w .NET 4, może 3.5 i poniżej)

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

Trzeba tylko wiedzieć podpis funkcji ty dzwonisz i zastąpić działanie w powyższym przykładzie.

+0

To jest bardziej skomplikowane niż potrzebne. –

+0

@BenVoigt Nie jestem pewien, zgadzam się, że jest to bardziej skomplikowane niż napisanie wrappera do funkcji InvokeRepeating, która jest utrzymywana gdzie indziej. Jest to również dokładnie to, co zostało zasugerowane przez kilka komentarzy (http://joelabrahamsson.com/getting-property-and-method-names-using-static-reflection-in-c/) bez owijania ich w klasie Utility. – Michael

+0

Porównaj z komentarzem pmcoltrane ... –

4

Używam następujących w mojej bazie kodów, ekstrapolować dla 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); 
    } 
} 

Używaj go jak ten

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

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

Złóż enum Spośród współprogram masz:

public enum MyCoroutines 
{ 
Coroutine1, 
Coroutine2 
} 

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

ten sposób masz żadnego niebezpieczeństwa ortograficzny nazwy funkcji plus można znaleźć i zamień je z łatwością. Jest również bardzo dobry do odtwarzania dźwięków i animacji.

0

Oto moje zdanie na ten temat, ponieważ niedawno natknąłem się na ten problem w kontekście UnityEvent, które są również przechowywane i mogą być odzyskane tylko za pomocą nazwy metody. Moje podejście ma więcej sensu, gdy mamy do czynienia z obsługą zdarzeń z różnymi sygnaturami, ale działa również dla każdego Akcja i Wywołanie również.

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... 
} 

Jak inni wspomniano wcześniej, najprostszym sposobem pobierania MethodInfo uzyskać nazwę metody, znajduje się za pośrednictwem typu delegata takich jak działania. Nie chciałbym określać przeciążeń dla każdego typu delegata, takiego jak Action, Action, Action, ponieważ nie używam tych parametrów. Zamiast tego mogę po prostu rzucić dowolną metodę, którą przekazałbym jako parametr do poprawnego typu.