Şu anda yürütülen yöntemin adını bulmak için yansıma kullanabilir misiniz?


204

Başlığın dediği gibi: Yansıma, şu anda yürütülen yöntemin adını verebilir mi?

Heisenberg sorunu yüzünden tahmin etmemeye eğilimliyim. Mevcut yöntemin ne olduğunu değiştirmeden mevcut yöntemi anlatacak bir yöntemi nasıl çağırırsınız? Ama umarım birisi beni yanlış anlayabilir.

Güncelleme:

  • Bölüm 2: Bu, bir özellik kodunun içine bakmak için de kullanılabilir mi?
  • Bölüm 3: Performans nasıl olurdu?

Son Sonuç
MethodBase.GetCurrentMethod () hakkında öğrendim. Ayrıca sadece bir yığın izlemesi oluşturmakla kalmayıp, istersem ihtiyacım olan tam kareyi oluşturabileceğimi de öğrendim.

Bunu bir özellik içinde kullanmak için, 'set_' veya 'get_' öğesini kaldırmak için bir .Substring (4) almanız yeterlidir.


Joel, bunun eski bir soru olduğunu biliyorum, ama bir yöntemin tam çerçevesini yaratarak ne demek istiyorsun?
Abhijeet

Çağrı yığınındaki belirli bir öğeyi ifade eder: yığın izlemesinin önemli olan kısmı.
Joel Coehoorn

Yanıtlar:


120

.NET 4.5'ten itibaren [CallerMemberName] yazılımını da kullanabilirsiniz.

Örnek: bir özellik ayarlayıcı (2. bölümü cevaplamak için):

    protected void SetProperty<T>(T value, [CallerMemberName] string property = null)
    {
        this.propertyValues[property] = value;
        OnPropertyChanged(property);
    }

    public string SomeProperty
    {
        set { SetProperty(value); }
    }

Derleyici, çağrı sitelerinde eşleşen dize değişmezleri sağlar, bu nedenle temelde performans ek yükü yoktur.


3
Bu harika! StackFrame(1)Günlüğe kaydetme için diğer cevaplarda açıklanan yöntemi kullanıyordum , ki Jitter satırları başlatmaya karar verene kadar işe yaradı. Performans nedenlerinden dolayı satırlamayı önlemek için bu özelliği eklemek istemedim. Kullanılması [CallerMemberName]sorunu sabit bir yaklaşım. Teşekkürler!
Brian Rogers

5
[CallerMemberName], BCL yapısı paketlenmiş
Venson

2
Hata ayıklama modunda StackFrame (1) kullanmanın çalışması gerektiğini unutmayın. Ancak derleme sırasında serbest bırakma modunu kullanırken, bazı optimizasyonlar olabilir ve yığın beklediğiniz gibi olmayabilir.
Axel O'Connell

Bu, şu anda yürütülen yöntem yerine arayan üyeyi (örn. SomeProperty) döndürmez mi?
Lennart

1
Evet, setter'ı çağırmak bir çağrıya neden olur OnPropertyChanged("SomeProperty")ve olmazOnPropertyChanged("SetProperty")
John Nilsson

190

asyncYöntem olmayanlar için

System.Reflection.MethodBase.GetCurrentMethod().Name;

https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getcurrentmethod

Lütfen asyncyöntemler için "MoveNext" i döndüreceğini unutmayın .


8
Bunun her zaman beklenen sonuçları vermediğini unutmayın. Yani, küçük yöntemler veya özellikler genellikle sürüm derlemelerinde satır içine alınır; bu durumda sonuç, bunun yerine arayanın yöntem adı olur.
Abel

5
Bildiğim kadarıyla hayır. Çünkü çalışma zamanında, MSIL artık yürütme işaretçisinden kullanılamaz (JITted). Yöntemin adını biliyorsanız hala yansıma kullanabilirsiniz. Nokta, eğik olduğunda, şu anda yürütülen yöntem artık başka bir yöntemdir (yani, bir veya daha fazla yığın daha yüksek). Başka bir deyişle, yöntem ortadan kayboldu. Yönteminizi NoInlining ile işaretleseniz bile, hala kuyruk çağrısını optimize etme şansı var, bu durumda da gitti. Bununla birlikte, hata ayıklama derlemesinde çalışırken çalışır.
Abel

1
Satır içi önlemek için yöntemin üstüne [MethodImpl (MethodImplOptions.NoInlining)] özniteliğini ekleyin.
alex.peter

asyncYöntemin içinde , büyük olasılıkla yöntem adı olarak "MoveNext" ifadesini alırsınız.
Victor Yarema

47

Lex tarafından sağlanan snippet biraz uzuntu, bu yüzden önemli olana dikkat çekiyorum çünkü hiç kimse aynı tekniği kullanmadı:

string MethodName = new StackFrame(0).GetMethod().Name;

Bu, MethodBase.GetCurrentMethod () yöntemine aynı sonuçları döndürmelidir.Ancak yine de dikkat çekmeye değer çünkü önceki yöntem için dizin 1'i kullanarak kendi yönteminde bir kez uygulayabilir ve bir dizi farklı özellikten çağırabilirim. Ayrıca, tüm yığın izlemesi yerine yalnızca bir kare döndürür:

private string GetPropertyName()
{  //.SubString(4) strips the property prefix (get|set) from the name
    return new StackFrame(1).GetMethod().Name.Substring(4);
}

Bu da bir astar;)


yardımcı sınıfta genel statik dize GetPropertyName () olabilir? statik yöntem?
Kiquenet

2
Ed Guiness'in cevabı ile aynı: sürüm sürümlerinde yığın farklı olabilir ve ilk yöntem, satır içi veya kuyruk çağrısı optimizasyonu durumlarında mevcut yöntemle aynı olmayabilir.
Abel

Net 4.5 kullanıyorsanız, astar sorunuyla ilgili güzel bir yol için John Nilsson'un cevabına bakın.
Brian Rogers

bu, kabul edilen cevaptan ve yukarıdaki cevaptan daha iyi olabilir
T.Todua

16

Boş bir konsol programında Ana yöntemde bunu deneyin:

MethodBase method = MethodBase.GetCurrentMethod();
Console.WriteLine(method.Name);

Konsol Çıkışı:
Main


12

Evet kesinlikle.

Bir nesne manipüle etmek istiyorsanız ben aslında böyle bir işlevi kullanın:

public static T CreateWrapper<T>(Exception innerException, params object[] parameterValues) where T : Exception, new()
{
    if (parameterValues == null)
    {
        parameterValues = new object[0];
    }

    Exception exception   = null;
    StringBuilder builder = new StringBuilder();
    MethodBase method     = new StackFrame(2).GetMethod();
    ParameterInfo[] parameters = method.GetParameters();
    builder.AppendFormat(CultureInfo.InvariantCulture, ExceptionFormat, new object[] { method.DeclaringType.Name, method.Name });
    if ((parameters.Length > 0) || (parameterValues.Length > 0))
    {
        builder.Append(GetParameterList(parameters, parameterValues));
    }

    exception = (Exception)Activator.CreateInstance(typeof(T), new object[] { builder.ToString(), innerException });
    return (T)exception;
}

Bu hat:

MethodBase method     = new StackFrame(2).GetMethod();

Çağrı yöntemini bulmak için yığın çerçevesini açar, ardından genel bir hata raporlama işlevi için kendisine aktarılan parametre bilgi değerlerini elde etmek için yansımayı kullanırız. Geçerli yöntemi almak için bunun yerine geçerli yığın çerçevesini (1) kullanın.

Diğerlerinin geçerli yöntemler adı için söylediği gibi şunları da kullanabilirsiniz:

MethodBase.GetCurrentMethod()

Yığın yürümeyi tercih ederim, çünkü bu yönteme dahili olarak bakarsanız, sadece bir StackCrawlMark oluşturur. Yığına hitap etmek benim için doğrudan daha net görünüyor

Post 4.5 artık yöntem adının bir dizesini almak için yöntem parametrelerinin bir parçası olarak [CallerMemberNameAttribute] kullanabilirsiniz - bu bazı senaryolarda yardımcı olabilir (ama gerçekten yukarıdaki örnekte)

public void Foo ([CallerMemberName] string methodName = null)

Bu daha önce INTifyPropertyChanged desteği için daha önce olay kodunuz aracılığıyla tüm dizeleri çöp vardı bir çözüm gibi görünüyordu.


Aptalca değil; Onları basitçe geçtim. Muhtemelen bakmayı daha kolay hale getirmek için bir şeyler yapabilirsin, ancak ödüllendirme çabası onu basit tutmayı tercih ediyor gibiydi. Aslında dev, yöntem imzasının parametre listesine kopyalanır (elbette türleri kaldırmak).
Lex

nedir: ExceptionFormat ve GetParameterList?
Kiquenet

Cevabın çok geç ama: ExceptionFormat sabit bir dize biçimidir ve GetParameterList, parametreleri değerlerle formatlayan basit bir işlevdir (bu satır içinde yapabilirsiniz)
Lex

11

Yöntem adını almanın yollarını karşılaştırma - LinqPad'de rasgele bir zamanlama yapısı kullanarak :

KOD

void Main()
{
    // from http://blogs.msdn.com/b/webdevelopertips/archive/2009/06/23/tip-83-did-you-know-you-can-get-the-name-of-the-calling-method-from-the-stack-using-reflection.aspx
    // and /programming/2652460/c-sharp-how-to-get-the-name-of-the-current-method-from-code

    var fn = new methods();

    fn.reflection().Dump("reflection");
    fn.stacktrace().Dump("stacktrace");
    fn.inlineconstant().Dump("inlineconstant");
    fn.constant().Dump("constant");
    fn.expr().Dump("expr");
    fn.exprmember().Dump("exprmember");
    fn.callermember().Dump("callermember");

    new Perf {
        { "reflection", n => fn.reflection() },
        { "stacktrace", n => fn.stacktrace() },
        { "inlineconstant", n => fn.inlineconstant() },
        { "constant", n => fn.constant() },
        { "expr", n => fn.expr() },
        { "exprmember", n => fn.exprmember() },
        { "callermember", n => fn.callermember() },
    }.Vs("Method name retrieval");
}

// Define other methods and classes here
class methods {
    public string reflection() {
        return System.Reflection.MethodBase.GetCurrentMethod().Name;
    }
    public string stacktrace() {
        return new StackTrace().GetFrame(0).GetMethod().Name;
    }
    public string inlineconstant() {
        return "inlineconstant";
    }
    const string CONSTANT_NAME = "constant";
    public string constant() {
        return CONSTANT_NAME;
    }
    public string expr() {
        Expression<Func<methods, string>> ex = e => e.expr();
        return ex.ToString();
    }
    public string exprmember() {
        return expressionName<methods,string>(e => e.exprmember);
    }
    protected string expressionName<T,P>(Expression<Func<T,Func<P>>> action) {
        // https://stackoverflow.com/a/9015598/1037948
        return ((((action.Body as UnaryExpression).Operand as MethodCallExpression).Object as ConstantExpression).Value as MethodInfo).Name;
    }
    public string callermember([CallerMemberName]string name = null) {
        return name;
    }
}

SONUÇLAR

yansıma yansıma

yığın izi yığın izi

satır içi satır içi satır içi

sabit sabit

ifade e => e.expr ()

exprmember exprmember

callermember Main

Method name retrieval: (reflection) vs (stacktrace) vs (inlineconstant) vs (constant) vs (expr) vs (exprmember) vs (callermember) 

 154673 ticks elapsed ( 15.4673 ms) - reflection
2588601 ticks elapsed (258.8601 ms) - stacktrace
   1985 ticks elapsed (  0.1985 ms) - inlineconstant
   1385 ticks elapsed (  0.1385 ms) - constant
1366706 ticks elapsed (136.6706 ms) - expr
 775160 ticks elapsed ( 77.516  ms) - exprmember
   2073 ticks elapsed (  0.2073 ms) - callermember


>> winner: constant

exprVe callermemberyöntemlerinin "doğru" olmadığını unutmayın . Ve orada , yansımanın yığın izinden ~ 15 kat daha hızlı olduğu ilgili bir yorumun tekrarını görüyorsunuz .


9

EDIT: MethodBase muhtemelen içinde bulunduğunuz yöntemi elde etmek için daha iyi bir yoldur (tüm çağrı yığını aksine). Yine de satır içi çizgiden endişe duyarım.

Yöntem içinde bir StackTrace kullanabilirsiniz:

StackTrace st = new StackTrace(true);

Ve çerçevelere bakın:

// The first frame will be the method you want (However, see caution below)
st.GetFrames();

Ancak, yöntem satır içi ise, olduğunuzu düşündüğünüz yöntemin içinde olmayacağınızı unutmayın. Satırda kalmayı önlemek için bir özellik kullanabilirsiniz:

[MethodImpl(MethodImplOptions.NoInlining)]

Sürüm optimizasyonu nedeniyle satır içi, özellikle hata ayıklama ve Sürüm yapılandırmalarında kod farklı davranacağından özellikle zordur. Küçük mülklere dikkat edin, bunun en olası kurbanlarıdır.
DK.

Kullanmak woud Acaba neden new StackTrace(true)yerine new StackTrace(false). Bunu ayarlamak true, yığın izlemesinin dosya adını, satır numarasını vb. Yakalamayı denemesine neden olur ve bu da bu aramayı yavaşlatabilir. Aksi takdirde, güzel bir cevap
Ivaylo Slavov

6

Anlaşmanın basit yolu:

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;

System.Reflection kullanma bloğuna dahil edilmişse:

MethodBase.GetCurrentMethod().DeclaringType.FullName + "." + MethodBase.GetCurrentMethod().Name;

4

Buna ne dersiniz:

StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name

MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name


1
using System;
                    
public class Program
{
    public static void Main()
    {
        
        Console.WriteLine("1: {0} {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, System.Reflection.MethodBase.GetCurrentMethod().ReflectedType);
        OtherMethod();
    }
    
    public static void OtherMethod()
    {
        Console.WriteLine("2: {0} {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, System.Reflection.MethodBase.GetCurrentMethod().ReflectedType);
    }
}

Çıktı:

1: Main Program
2: OtherMethod Program

0

Bunu dene...

    /// <summary>
    /// Return the full name of method
    /// </summary>
    /// <param name="obj">Class that calls this method (use Report(this))</param>
    /// <returns></returns>
    public string Report(object obj)
    {
        var reflectedType = new StackTrace().GetFrame(1).GetMethod().ReflectedType;
        if (reflectedType == null) return null;

        var i = reflectedType.FullName;
        var ii = new StackTrace().GetFrame(1).GetMethod().Name;

        return string.Concat(i, ".", ii);
    }

0

Bunu basit bir statik sınıfla yaptım:

using System.Runtime.CompilerServices;
.
.
.
    public static class MyMethodName
        {
            public static string Show([CallerMemberName] string name = "")
            {
                return name;
            }
        }

sonra kodunuzda:

private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = MyMethodName.Show();
        }

-1
new StackTrace().ToString().Split("\r\n",StringSplitOptions.RemoveEmptyEntries)[0].Replace("at ","").Trim()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.