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

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

سؤال

عند تسجيل الدخول إلى C#، كيف يمكنني تعلم اسم الطريقة التي تسمى الطريقة الحالية؟ أنا أعرف كل شيء عن System.Reflection.MethodBase.GetCurrentMethod(), ، لكني أريد أن أذهب خطوة واحدة تحت هذا في تتبع المكدس. لقد فكرت في تحليل تتبع المكدس ، لكنني آمل أن أجد طريقة أكثر وضوحًا ، شيء مثل Assembly.GetCallingAssembly() ولكن للطرق.

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

المحلول

جرب هذا:

using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();

// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

إنها من احصل على طريقة الاتصال باستخدام الانعكاس [C#.

نصائح أخرى

في C# 5 ، يمكنك الحصول على هذه المعلومات باستخدام معلومات المتصل:

//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "") 
{ 
    Console.WriteLine(callerName + "called me."); 
} 

يمكنك أيضا الحصول على [CallerFilePath] و [CallerLineNumber].

يمكنك استخدام معلومات المتصل والمعلمات الاختيارية:

public static string WhoseThere([CallerMemberName] string memberName = "")
{
       return memberName;
}

يوضح هذا الاختبار هذا:

[Test]
public void Should_get_name_of_calling_method()
{
    var methodName = CachingHelpers.WhoseThere();
    Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}

على الرغم من أن StackTrace يعمل بسرعة كبيرة جدًا ولن يكون مشكلة في الأداء في معظم الحالات ، فستظل معلومات المتصل أسرع بكثير. في عينة من 1000 تكرار ، قمت بتسجيلها أسرع 40 مرة.

يمكننا تحسين رمز السيد الأسد (الإجابة الحالية المقبولة) قليلاً عن طريق إنشاء إطار فقط نحتاجه بالفعل بدلاً من المكدس بأكمله:

new StackFrame(1).GetMethod().Name;

قد يكون أداء هذا أفضل قليلاً ، على الرغم من أنه لا يزال يتعين عليه استخدام المكدس الكامل لإنشاء هذا الإطار المفرد. أيضًا ، لا يزال يحتوي على نفس التحذيرات التي أشار إليها أليكس ليمان (قد يفسد المحسّن/الكود الأصلي النتائج). أخيرًا ، قد ترغب في التحقق للتأكد من ذلك new StackFrame(1) أو .GetFrame(1) لا تعود null, ، من غير المحتمل أن يبدو هذا الاحتمال.

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

بشكل عام ، يمكنك استخدام System.Diagnostics.StackTrace الفصل للحصول على ملف System.Diagnostics.StackFrame, ، ثم استخدم GetMethod() طريقة للحصول على ملف System.Reflection.MethodBase هدف. ومع ذلك ، هناك بعض التحذيرات لهذا النهج:

  1. إنه يمثل مدة العرض المكدس - يمكن أن تضمن التحسينات طريقة ، وستفعل ليس انظر هذه الطريقة في تتبع المكدس.
  2. فإنه سوف ليس أظهر أي إطارات أصلية ، لذلك إذا كانت هناك فرصة حتى يتم استدعاء طريقتك بواسطة طريقة أصلية ، فهذه الإرادة ليس العمل ، وهناك طريقة متاحة حاليًا للقيام بذلك.

(ملاحظة: أنا أتوسع فقط الاجابة المقدمة من Firas الأسد.)

خلاصة سريعة من النهج 2 مع مقارنة السرعة كونها الجزء المهم.

http://geekswithblogs.net/blackrabbitcoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx

تحديد المتصل في وقت الترجمة

static void Log(object message, 
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}

تحديد المتصل باستخدام المكدس

static void Log(object message)
{
    // frame 1, true for source info
    StackFrame frame = new StackFrame(1, true);
    var method = frame.GetMethod();
    var fileName = frame.GetFileName();
    var lineNumber = frame.GetFileLineNumber();

    // we'll just use a simple Console write for now    
    Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}

مقارنة بين النهج 2

Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms

هكذا ترى ، أن استخدام السمات أسرع بكثير! ما يقرب من 25x أسرع في الواقع.

اعتبارًا من .NET 4.5 يمكنك استخدامه معلومات المتصل صفات:

  • CallerFilePath - الملف المصدر الذي يسمى الوظيفة ؛
  • CallerLineNumber - سطر الكود الذي يسمى الوظيفة ؛
  • CallerMemberName - عضو دعا الوظيفة.

    public void WriteLine(
        [CallerFilePath] string callerFilePath = "", 
        [CallerLineNumber] long callerLineNumber = 0,
        [CallerMemberName] string callerMember= "")
    {
        Debug.WriteLine(
            "Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}", 
            callerFilePath,
            callerLineNumber,
            callerMember);
    }
    

 

هذا المرفق موجود أيضًا في ".NET Core" و ".NET Standard".

مراجع

  1. Microsoft - معلومات المتصل (C#)
  2. مايكروسوفت - CallerFilePathAttribute فصل
  3. مايكروسوفت - CallerLineNumberAttribute فصل
  4. مايكروسوفت - CallerMemberNameAttribute فصل

لاحظ أن القيام بذلك سيكون غير موثوق به في رمز الإصدار ، بسبب التحسين. بالإضافة إلى ذلك ، لن يسمح لك تشغيل التطبيق في وضع Sandbox (مشاركة الشبكة) بالاستيلاء على إطار المكدس على الإطلاق.

انصح الجانب برمجة (AOP) ، مثل postsharp, ، بدلاً من أن يتم استدعاؤه من التعليمات البرمجية الخاصة بك ، يعدل التعليمات البرمجية الخاصة بك ، وبالتالي يعرف مكانه في جميع الأوقات.

/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
   return GetCallingMethod("GetCallingMethod");
}

/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
   string str = "";
   try
   {
      StackTrace st = new StackTrace();
      StackFrame[] frames = st.GetFrames();
      for (int i = 0; i < st.FrameCount - 1; i++)
      {
         if (frames[i].GetMethod().Name.Equals(MethodAfter))
         {
            if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
            {
               str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
               break;
            }
         }
      }
   }
   catch (Exception) { ; }
   return str;
}

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

internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
    Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}

سيطبع هذا التاريخ والوقت الحاليين ، يليه "NamePace.ClassName.MethodName" وينتهي بـ ": text".
إخراج العينة:

6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized

استخدام العينة:

Logger.WriteInformation<MainWindow>("MainWindow initialized");

ربما كنت تبحث عن شيء مثل هذا:

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
private static MethodBase GetCallingMethod()
{
  return new StackFrame(2, false).GetMethod();
}

private static Type GetCallingType()
{
  return new StackFrame(2, false).GetMethod().DeclaringType;
}

فصل رائع هنا: http://www.csharp411.com/c-get-calling-method/

هناك طريقة أخرى استخدمتها وهي إضافة معلمة إلى الطريقة المعنية. على سبيل المثال ، بدلاً من void Foo(), ، استعمال void Foo(string context). ثم تمر في بعض السلسلة الفريدة التي تشير إلى سياق الاتصال.

إذا كنت بحاجة فقط إلى المتصل/السياق للتطوير ، فيمكنك إزالة param قبل الشحن.

StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

سيكون كافيا ، على ما أعتقد.

ألق نظرة على اسم طريقة التسجيل في .NET. احذر من استخدامه في رمز الإنتاج. StackFrame قد لا يكون موثوقا ...

يمكننا أيضًا استخدام Lambda للعثور على المتصل.

لنفترض أن لديك طريقة محددة من قبلك:

public void MethodA()
    {
        /*
         * Method code here
         */
    }

وتريد أن تجدها المتصل.

1. قم بتغيير توقيع الطريقة بحيث يكون لدينا معلمة من نوع العمل (ستعمل FUNC أيضًا):

public void MethodA(Action helperAction)
        {
            /*
             * Method code here
             */
        }

2. لا يتم إنشاء أسماء Lambda بشكل عشوائي. يبدو أن القاعدة هي:>u003CCallerMethodName> __x حيث يتم استبدال callermethodname بالوظيفة السابقة و x هو فهرس.

private MethodInfo GetCallingMethodInfo(string funcName)
    {
        return GetType().GetMethod(
              funcName.Substring(1,
                                funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

3. عندما نسمي Methoda ، يجب إنشاء معلمة الإجراء/FUNC بواسطة طريقة المتصل. مثال:

MethodA(() => {});

4. Inside Methoda يمكننا الآن استدعاء وظيفة المساعد المحددة أعلاه والعثور على MethodInfo لطريقة المتصل.

مثال:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);

للحصول على اسم الطريقة واسم الفصل جرب هذا:

    public static void Call()
    {
        StackTrace stackTrace = new StackTrace();

        var methodName = stackTrace.GetFrame(1).GetMethod();
        var className = methodName.DeclaringType.Name.ToString();

        Console.WriteLine(methodName.Name + "*****" + className );
    }

معلومات إضافية إلى Firas Assaad الإجابة.

لقد استخدمت new StackFrame(1).GetMethod().Name; في .NET CORE 2.1 مع حقن التبعية وأحصل على طريقة الاتصال على أنها "بداية".

حاولت مع [System.Runtime.CompilerServices.CallerMemberName] string callerName = ""وهو يعطيني طريقة الاتصال الصحيحة

var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top