คุณสามารถใช้การสะท้อนเพื่อค้นหาชื่อของวิธีการดำเนินการในปัจจุบันได้หรือไม่?


202

เช่นเดียวกับชื่อ says: สามารถสะท้อนให้คุณชื่อของวิธีการดำเนินการปัจจุบัน

ฉันมีแนวโน้มที่จะเดาไม่ได้เพราะปัญหาของไฮเซนเบิร์ก คุณจะเรียกวิธีการที่จะบอกวิธีปัจจุบันโดยไม่เปลี่ยนวิธีการปัจจุบันได้อย่างไร แต่ฉันหวังว่าบางคนสามารถพิสูจน์ฉันผิดได้ที่นั่น

ปรับปรุง:

  • ส่วนที่ 2: สามารถใช้เพื่อดูโค้ดภายในของคุณสมบัติได้หรือไม่
  • ตอนที่ 3: การแสดงจะเป็นอย่างไร?

ผลลัพธ์สุดท้าย
ฉันเรียนรู้เกี่ยวกับ MethodBase.GetCurrentMethod () ฉันได้เรียนรู้ว่าไม่เพียง แต่ฉันสามารถสร้างการติดตามสแต็กฉันสามารถสร้างเฟรมที่แน่นอนที่ฉันต้องการได้หากต้องการ

หากต้องการใช้สิ่งนี้ภายในพร็อพเพอร์ตี้ให้ใช้. Substring (4) เพื่อลบ 'set_' หรือ 'get_'


โจเอลฉันรู้คำถามเก่า แต่คุณหมายถึงอะไรโดยการสร้างเฟรมที่แน่นอนของวิธี?
Abhijeet

มันหมายถึงรายการที่เฉพาะเจาะจงในการโทรสแต็ค: ส่วนของการติดตามสแต็กที่มีความสำคัญ
Joel Coehoorn

คำตอบ:


119

ในฐานะ. NET 4.5 คุณสามารถใช้[CallerMemberName]

ตัวอย่าง: setter คุณสมบัติ (เพื่อตอบส่วนที่ 2):

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

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

คอมไพเลอร์จะจัดหาตัวอักษรสตริงที่ตรงกันที่ callsites ดังนั้นโดยทั่วไปจะไม่มีค่าใช้จ่ายด้านประสิทธิภาพ


3
มันเยี่ยมมาก! ฉันใช้StackFrame(1)วิธีที่อธิบายไว้ในคำตอบอื่น ๆ สำหรับการบันทึกซึ่งดูเหมือนว่าจะทำงานได้จนกว่า Jitter ตัดสินใจที่จะเริ่มต้นทำสิ่งต่างๆ ฉันไม่ต้องการเพิ่มแอตทริบิวต์เพื่อป้องกันการอินไลน์ด้วยเหตุผลด้านประสิทธิภาพ การใช้[CallerMemberName]วิธีแก้ไขปัญหา ขอบคุณ!
Brian Rogers

5
[CallerMemberName] สามารถใช้งานได้ที่ net 4 พร้อมBCL build build
Venson

2
พิจารณาว่าการใช้ StackFrame (1) ในโหมดแก้ไขข้อบกพร่องควรทำงาน แต่เมื่อใช้โหมดรีลีสเมื่อรวบรวมอาจมีการปรับให้เหมาะสมบางอย่างและสแต็กอาจไม่ใช่สิ่งที่คุณคาดหวัง
Axel O'Connell

สิ่งนี้จะไม่ส่งคืนสมาชิกที่โทร (เช่น SomeProperty) แทนที่จะเป็นวิธีการดำเนินการปัจจุบัน
Lennart

1
ใช่การเรียกผู้ตั้งค่าจะส่งผลให้มีการโทรหาOnPropertyChanged("SomeProperty")และไม่OnPropertyChanged("SetProperty")
John Nilsson

189

สำหรับasyncวิธีที่ไม่ใช่วิธีหนึ่งสามารถใช้

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

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

โปรดจำไว้ว่าสำหรับasyncวิธีการมันจะคืนค่า "MoveNext"


8
โปรดทราบว่าสิ่งนี้อาจไม่ได้ผลลัพธ์ตามที่คาดหวังเสมอไป เช่นวิธีการขนาดเล็กหรือคุณสมบัติมักจะ inline ในรุ่นที่วางจำหน่ายซึ่งในกรณีนี้ผลที่ได้จะเป็นชื่อวิธีการของผู้โทรแทน
Abel

5
เท่าที่ฉันรู้ไม่ เนื่องจากในขณะรันไทม์ MSIL จะไม่สามารถใช้งานได้อีกต่อไปจากตัวชี้การเรียกใช้งาน (เป็น JITted) คุณยังสามารถใช้การสะท้อนหากคุณทราบชื่อของวิธีการ จุดคือเมื่อ inlined วิธีการดำเนินการในขณะนี้เป็นวิธีอื่น (เช่นหนึ่งหรือมากกว่าที่สูงขึ้นสแต็ค) ในคำอื่น ๆ วิธีการที่หายไป แม้ว่าคุณจะทำเครื่องหมายวิธีการของคุณด้วย NoInlining แต่ก็ยังมีโอกาสที่จะได้รับการเพิ่มประสิทธิภาพการโทรแบบหางซึ่งในกรณีนี้ก็จะหายไปด้วย มันจะทำงานอย่างไรในขณะที่อยู่ในการตรวจแก้จุดบกพร่อง
Abel

1
เพื่อหลีกเลี่ยงการอินไลน์เพิ่มแอตทริบิวต์ [MethodImpl (MethodImplOptions.NoInlining)] ที่ด้านบนของวิธีการ
alex.peter

asyncวิธีการภายในคุณมักจะได้รับ "MoveNext" เป็นชื่อวิธี
Victor Yarema

46

ตัวอย่างข้อมูลที่ Lex ให้มานั้นยาวไปหน่อยดังนั้นฉันจึงชี้ให้เห็นถึงส่วนที่สำคัญเนื่องจากไม่มีใครใช้เทคนิคเดียวกันนี้:

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

สิ่งนี้ควรส่งคืนผลลัพธ์ที่เหมือนกันให้กับMethodBase.GetCurrentMethod ().เทคนิคชื่อแต่ก็ยังคุ้มค่าที่จะชี้ให้เห็นเพราะฉันสามารถใช้วิธีนี้ในวิธีของตัวเองโดยใช้ดัชนี 1 สำหรับวิธีการก่อนหน้าและเรียกมันจากคุณสมบัติที่แตกต่างกัน นอกจากนี้มันจะคืนค่าเฟรมเดียวแทนที่จะเป็นสแต็กการติดตามทั้งหมด:

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

มันเป็นหนึ่งซับด้วยเช่นกัน;)


สามารถเป็นสาธารณะคงสตริง GetPropertyName () ในคลาสผู้ช่วยหรือไม่ วิธีการแบบคงที่?
Kiquenet

2
เช่นเดียวกับคำตอบของ Ed Guiness: สแต็กอาจแตกต่างกันใน build build และวิธีแรกอาจไม่เหมือนกับวิธีปัจจุบันในกรณีของการปรับ inline หรือ tail tail optimization
Abel

ดูคำตอบของ John Nilsson สำหรับวิธีที่ดีในการแก้ไขปัญหาการ inline ถ้าคุณใช้. Net 4.5
Brian Rogers

นี่อาจดีกว่าคำตอบที่ยอมรับและคำตอบข้างบนด้วย
T.Todua

16

ลองวิธีนี้ในวิธีการหลักในโปรแกรมคอนโซลที่ว่างเปล่า:

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

เอาต์พุตคอนโซล:
Main


12

ใช่แน่นอน.

หากคุณต้องการให้วัตถุจัดการฉันใช้ฟังก์ชั่นเช่นนี้:

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

สายนี้:

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

เดินขึ้นไปบนสแต็กเฟรมเพื่อค้นหาวิธีการเรียกใช้จากนั้นเราใช้การสะท้อนเพื่อรับค่าข้อมูลพารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชันการรายงานข้อผิดพลาดทั่วไป ในการรับเมธอดปัจจุบันให้ใช้สแต็กเฟรมปัจจุบัน (1) แทน

ตามที่คนอื่นพูดถึงชื่อวิธีการปัจจุบันคุณสามารถใช้:

MethodBase.GetCurrentMethod()

ฉันชอบเดินสแต็คเพราะถ้าดูภายในวิธีนั้นมันก็สร้าง StackCrawlMark อยู่ดี การพูดกับสแต็คโดยตรงเห็นได้ชัดสำหรับฉัน

โพสต์ 4.5 ตอนนี้คุณสามารถใช้ [CallerMemberNameAttribute] เป็นส่วนหนึ่งของพารามิเตอร์เมธอดเพื่อรับสตริงของชื่อเมธอดซึ่งอาจช่วยได้ในบางสถานการณ์ (แต่จริงๆแล้วพูดตามตัวอย่างข้างต้น)

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

สิ่งนี้ดูเหมือนจะเป็นวิธีการแก้ปัญหาสำหรับการสนับสนุน INotifyPropertyChanged ซึ่งก่อนหน้านี้คุณมีสตริงที่เกลื่อนไปทั่วผ่านรหัสเหตุการณ์ของคุณ


ไม่โง่ ฉันแค่ส่งพวกมันเข้าไปคุณอาจจะทำอะไรบางอย่างเพื่อให้ดูง่ายขึ้น แต่ความพยายามในการให้รางวัลอัตราส่วนดูเหมือนจะช่วยให้มันง่ายขึ้น เป็นหลัก dev เพียงแค่คัดลอกในรายการพารามิเตอร์ของวิธีการลายเซ็น (ลบประเภทของหลักสูตร)
Lex

มันคืออะไร: ExceptionFormat และ GetParameterList?
Kiquenet

วิธีที่ปลายในการตอบกลับ แต่: ExceptionFormat เป็นรูปแบบสตริงคงที่และ GetParameterList เป็นฟังก์ชั่นเพียงว่ารูปแบบพารามิเตอร์ที่มีค่า (คุณสามารถทำแบบอินไลน์นี้)
ไฟแนนเชี่

11

การเปรียบเทียบวิธีรับชื่อเมธอด - โดยใช้การกำหนดเวลาโดยพลการใน LinqPad:

รหัส

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

ผล

การสะท้อน กลับ

stacktrace ส แต็ค

inlineconstant inlineconstant

คงที่ คงที่

expr e => e.expr ()

exprmember exprmember

สมาชิก หลัก

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

โปรดทราบว่าexprและcallermemberวิธีการนั้นค่อนข้าง "ถูกต้อง" และที่นั่นคุณเห็นการซ้ำซ้อนของความคิดเห็นที่เกี่ยวข้องว่าการสะท้อนนั้นเร็วกว่าสแต็คเทรซ ~ 15 เท่า


9

แก้ไข: MethodBase น่าจะเป็นวิธีที่ดีกว่าในการรับเมธอดที่คุณใช้ ฉันยังคงกังวลเกี่ยวกับการทำอินไลน์

คุณสามารถใช้ StackTrace ภายในวิธีการ:

StackTrace st = new StackTrace(true);

และดูที่เฟรม:

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

อย่างไรก็ตามโปรดทราบว่าหากวิธีการดังกล่าวมีการ inline คุณจะไม่ได้อยู่ภายในวิธีการที่คุณคิดว่าคุณเป็น คุณสามารถใช้แอตทริบิวต์เพื่อป้องกันการอินไลน์ได้:

[MethodImpl(MethodImplOptions.NoInlining)]

แบบอินไลน์เนื่องจากการปรับให้เหมาะสมของ Release นั้นมีความยุ่งยากเป็นพิเศษเนื่องจากรหัสจะทำงานแตกต่างกันในการกำหนดค่า Debug และ Release ระวังคุณสมบัติเล็ก ๆ พวกเขาเป็นผู้ที่ตกเป็นเหยื่อของสิ่งนี้มากที่สุด
DK

ผมสงสัยว่าทำไม woud คุณใช้แทนnew StackTrace(true) new StackTrace(false)การตั้งค่าที่trueจะทำให้การติดตามสแต็กพยายามจับชื่อไฟล์หมายเลขบรรทัดและอื่น ๆ ซึ่งอาจทำให้การเรียกนี้ช้าลง มิฉะนั้นคำตอบที่ดี
Ivaylo Slavov

6

วิธีง่ายๆในการจัดการคือ:

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

หาก System.Reflection รวมอยู่ในบล็อกการใช้:

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

4

เกี่ยวกับสิ่งนี้:

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


0

ลองสิ่งนี้ ...

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

ฉันเพิ่งทำสิ่งนี้กับคลาสสแตติกแบบง่าย:

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

จากนั้นในรหัสของคุณ:

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.