هل يمكنك استخدام الانعكاس للعثور على اسم طريقة التنفيذ الحالية؟

StackOverflow https://stackoverflow.com/questions/44153

  •  09-06-2019
  •  | 
  •  

سؤال

كما يقول العنوان:هل يمكن أن يمنحك الانعكاس اسم طريقة التنفيذ الحالية.

أنا أميل إلى تخمين لا، بسبب مشكلة هايزنبرغ.كيف يمكنك استدعاء الطريقة التي ستخبرك بالطريقة الحالية دون تغيير الطريقة الحالية؟لكني آمل أن يتمكن شخص ما من إثبات خطأي هناك.

تحديث:

  • الجزء 2:هل يمكن استخدام هذا للبحث داخل الكود الخاص بخاصية ما أيضًا؟
  • الجزء 3:كيف سيكون الأداء؟

النتيجة النهائية
لقد تعلمت عن MethodBase.GetCurrentMethod().لقد تعلمت أيضًا أنه لا يمكنني إنشاء تتبع مكدس فحسب، بل يمكنني فقط إنشاء الإطار الدقيق الذي أحتاجه إذا أردت ذلك.

لاستخدام هذا داخل خاصية ما، ما عليك سوى استخدام .Substring(4) لإزالة 'set_' أو 'get_'.

هل كانت مفيدة؟

المحلول

اعتبارًا من .NET 4.5، يمكنك أيضًا استخدام [اسم عضو المتصل]

مثال:محدد الخاصية (للإجابة على الجزء 2):

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

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

سيوفر المترجم سلسلة حرفية مطابقة في مواقع الاتصال، لذلك لا يوجد أي حمل للأداء بشكل أساسي.

نصائح أخرى

كان المقتطف الذي قدمه Lex طويلًا بعض الشيء، لذا فأنا أشير إلى الجزء المهم حيث لم يستخدم أي شخص آخر نفس الأسلوب بالضبط:

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

يجب أن يؤدي هذا إلى إرجاع نتائج متطابقة إلى ملف MethodBase.GetCurrentMethod().Name هذه التقنية، ولكن لا يزال الأمر يستحق الإشارة إليه لأنه يمكنني تنفيذ ذلك مرة واحدة بطريقته الخاصة باستخدام الفهرس 1 لـ سابق طريقة ونسميها من عدد من الخصائص المختلفة.كما أنه يُرجع إطارًا واحدًا فقط بدلاً من تتبع المكدس بالكامل:

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

وهي مفردة أيضاً ;)

جرب هذا داخل الطريقة الرئيسية في برنامج وحدة تحكم فارغ:

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

إخراج وحدة التحكم:
Main

نعم بالتأكيد.

إذا كنت تريد التعامل مع كائن ما، فأنا في الواقع أستخدم وظيفة مثل هذه:

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 حيث كان لديك في السابق سلاسل متناثرة في جميع أنحاء رمز الحدث الخاص بك.

مقارنة الطرق للحصول على اسم الطريقة - باستخدام بناء التوقيت التعسفي في لينكباد:

شفرة

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 https://stackoverflow.com/questions/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;
    }
}

نتائج

انعكاسانعكاس

تتبع المكدستتبع المكدس

inlineconstantinlineconstant

ثابتثابت

EXPRe => e.expr()

com.exprmembercom.exprmember

com.callermemberرئيسي

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 مرة من تتبع المكدس.

يحرر:من المحتمل أن تكون MethodBase طريقة أفضل للحصول على الطريقة التي تستخدمها (على عكس مجموعة الاستدعاءات بأكملها).ما زلت أشعر بالقلق بشأن التضمين.

يمكنك استخدام StackTrace ضمن الطريقة:

StackTrace st = new StackTrace(true);

والنظر إلى الإطارات:

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

ومع ذلك، انتبه إلى أنه إذا كانت الطريقة مضمنة، فلن تكون داخل الطريقة التي تعتقد أنك موجود فيها.يمكنك استخدام سمة لمنع التضمين:

[MethodImpl(MethodImplOptions.NoInlining)]

طريقة التعامل البسيطة هي:

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

إذا تم تضمين System.Reflection في كتلة الاستخدام:

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

ماذا عن هذا:

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

أعتقد أنه يجب أن تكون قادرًا على الحصول على ذلك من خلال إنشاء ملف تتبع المكدس.أو كما @حافة و @لارس محلوم أذكر، MethodBase.GetCurrentMethod()

إذا كنت تحتاج فقط إلى اسم السلسلة للطريقة، فيمكنك استخدام Expressions.يرى http://joelabrahamsson.com/entry/getting-property-and-method-names-using-static-reflection-in-c-sharp

جرب هذا...

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

لقد فعلت هذا للتو مع فئة ثابتة بسيطة:

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();
        }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top