Bạn có thể sử dụng sự phản chiếu để tìm tên của phương thức hiện đang thực hiện không?


202

Giống như tiêu đề nói: Sự phản chiếu có thể cho bạn tên của phương thức hiện đang thực thi.

Tôi có khuynh hướng đoán không phải, vì vấn đề Heisenberg. Làm thế nào để bạn gọi một phương thức sẽ cho bạn biết phương thức hiện tại mà không thay đổi phương thức hiện tại là gì? Nhưng tôi hy vọng ai đó có thể chứng minh tôi sai ở đó.

Cập nhật:

  • Phần 2: Điều này có thể được sử dụng để xem mã bên trong cho một tài sản không?
  • Phần 3: Buổi biểu diễn sẽ như thế nào?

Kết quả cuối cùng
tôi đã học về Phương thứcBase.GetCienMethod (). Tôi cũng học được rằng không chỉ tôi có thể tạo theo dõi ngăn xếp, tôi chỉ có thể tạo khung chính xác mà tôi cần nếu tôi muốn.

Để sử dụng điều này trong một thuộc tính, chỉ cần lấy một chuỗi .Sub chuỗi (4) để xóa 'set_' hoặc 'get_'.


Joel, tôi biết đó là một câu hỏi cũ, nhưng ý bạn là gì khi tạo khung chính xác của một phương thức?
Abhijeet

Nó đề cập đến một mục cụ thể trong ngăn xếp cuộc gọi: phần của dấu vết ngăn xếp có vấn đề.
Joel Coehoorn

Câu trả lời:


119

Kể từ .NET 4.5, bạn cũng có thể sử dụng [CallerMemberName]

Ví dụ: bộ setter (để trả lời phần 2):

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

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

Trình biên dịch sẽ cung cấp các chuỗi ký tự phù hợp tại các cuộc gọi, do đó về cơ bản không có chi phí hiệu năng.


3
Điều đó thật tuyệt! Tôi đã sử dụng StackFrame(1)phương pháp được mô tả trong các câu trả lời khác để ghi nhật ký, có vẻ như hoạt động cho đến khi Jitter quyết định bắt đầu nội tuyến. Tôi không muốn thêm thuộc tính để ngăn chặn nội tuyến vì lý do hiệu suất. Sử dụng [CallerMemberName]phương pháp đã khắc phục vấn đề. Cảm ơn!
Brian Rogers

5
[CallerMemberName] có sẵn ở mạng 4 với bản dựng BCL được đóng gói
Venson

2
Hãy tính đến việc sử dụng StackFrame (1) trong chế độ gỡ lỗi sẽ hoạt động. Nhưng khi sử dụng chế độ phát hành khi biên dịch, có thể có một số tối ưu hóa và ngăn xếp có thể không như bạn mong đợi.
Axel O'Connell

Điều này sẽ không trả lại thành viên gọi điện (tức là someProperty), thay vì phương thức hiện đang thực thi?
Lennart

1
Có, việc gọi trình cài đặt sẽ dẫn đến một cuộc gọi đến OnPropertyChanged("SomeProperty")và khôngOnPropertyChanged("SetProperty")
John Nilsson

189

Đối với asyncphương pháp không phải người ta có thể sử dụng

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

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

Hãy nhớ rằng đối với asynccác phương thức, nó sẽ trả về "MoveNext".


8
Hãy lưu ý rằng điều này không phải lúc nào cũng mang lại kết quả như mong đợi. Tức là, các phương thức hoặc thuộc tính nhỏ thường được nội tuyến trong các bản dựng phát hành, trong trường hợp đó, kết quả sẽ là tên phương thức của người gọi thay thế.
Abel

5
Theo tôi biết, không. Bởi vì trong thời gian chạy, MSIL không còn khả dụng nữa từ con trỏ thực thi (đó là JITted). Bạn vẫn có thể sử dụng sự phản chiếu nếu bạn biết tên của phương thức. Vấn đề là, khi được nội tuyến, phương thức hiện đang thực thi bây giờ là một phương thức khác (nghĩa là, một hoặc nhiều hơn trong ngăn xếp). Nói cách khác, phương thức biến mất. Ngay cả khi bạn đánh dấu phương thức của mình bằng NoInlining, vẫn có khả năng nó được tối ưu hóa cuộc gọi đuôi, trong trường hợp đó cũng biến mất. Nó sẽ hoạt động, tuy nhiên, trong khi xây dựng gỡ lỗi.
Abel

1
Để tránh nội tuyến thêm thuộc tính [MethodImpl (MethodImplOptions.NoInlining)] trên đầu phương thức.
alex.peter

asyncPhương thức bên trong rất có thể bạn sẽ lấy "MoveNext" làm tên phương thức.
Victor Yarema

46

Đoạn trích do Lex cung cấp hơi dài, vì vậy tôi đang chỉ ra phần quan trọng vì không ai khác sử dụng cùng một kỹ thuật:

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

Điều này sẽ trả về kết quả giống hệt với kỹ thuật NameBase.GetCienMethod (). Nhưng vẫn đáng để chỉ ra bởi vì tôi có thể thực hiện điều này một lần trong phương thức riêng của mình bằng cách sử dụng chỉ mục 1 cho phương thức trước đó và gọi nó từ một số thuộc tính khác nhau. Ngoài ra, nó chỉ trả về một khung chứ không phải toàn bộ dấu vết ngăn xếp:

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

Nó cũng là một lớp lót;)


có thể là chuỗi tĩnh công khai GetPropertyName () trong một lớp trợ giúp? Phương pháp tĩnh?
Kiquenet

2
Giống như câu trả lời của Ed Guiness: ngăn xếp có thể khác nhau trong các bản dựng phát hành và phương thức đầu tiên có thể không giống với phương thức hiện tại trong các trường hợp tối ưu hóa cuộc gọi nội tuyến hoặc đuôi.
Abel

Xem câu trả lời của John Nilsson để biết cách giải quyết vấn đề nội tuyến nếu bạn đang sử dụng .Net 4.5.
Brian Rogers

điều này có thể tốt hơn câu trả lời được chấp nhận và câu trả lời trên cũng vậy
T.Todua

16

Hãy thử điều này bên trong phương thức Main trong một chương trình điều khiển trống:

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

Bảng điều khiển đầu ra:
Main


12

Vâng chắc chắn.

Nếu bạn muốn một đối tượng thao tác, tôi thực sự sử dụng một hàm như thế này:

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

Đường thẳng này:

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

Đi lên khung ngăn xếp để tìm phương thức gọi sau đó chúng ta sử dụng sự phản chiếu để thu được các giá trị thông tin tham số được truyền cho nó cho một hàm báo cáo lỗi chung. Để có được phương thức hiện tại, chỉ cần sử dụng khung stack hiện tại (1).

Như những người khác đã nói cho tên phương thức hiện tại, bạn cũng có thể sử dụng:

MethodBase.GetCurrentMethod()

Tôi thích đi bộ stack hơn vì nếu nhìn vào bên trong phương thức đó, nó chỉ đơn giản là tạo một StackCrawlMark. Việc giải quyết trực tiếp Stack có vẻ rõ ràng hơn với tôi

Bài 4.5 bây giờ bạn có thể sử dụng [CallerMemberNameAttribution] như một phần của tham số phương thức để lấy chuỗi tên phương thức - điều này có thể giúp ích trong một số trường hợp (nhưng thực sự nói ví dụ ở trên)

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

Đây dường như chủ yếu là một giải pháp cho hỗ trợ INotifyPropertyChanged, nơi trước đây bạn có các chuỗi rải rác thông qua mã sự kiện của bạn.


Không ngớ ngẩn; Tôi chỉ đơn giản là chuyển chúng vào. Bạn có thể có thể làm một cái gì đó để làm cho nó đơn giản hơn để xem xét nhưng nỗ lực để tỷ lệ phần thưởng dường như ủng hộ việc giữ cho nó đơn giản. Về cơ bản, nhà phát triển chỉ sao chép trong danh sách tham số của chữ ký phương thức (loại bỏ các loại khóa học).
Lex

nó là gì: ExceptionFormat và GetParameterList?
Kiquenet

Trả lời muộn nhưng: ExceptionFormat là định dạng chuỗi không đổi và GetParameterList là một hàm đơn giản định dạng các tham số với các giá trị (bạn có thể thực hiện nội tuyến này)
Lex

11

So sánh các cách để có được tên phương thức - sử dụng cấu trúc thời gian tùy ý trong 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;
    }
}

CÁC KẾT QUẢ

phản xạ phản chiếu

stacktrace stacktrace

inlineconstant inlineconstant

hằng số không đổi

expr e => e.expr ()

exprmember exprmember

cuộc gọi chính

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

Lưu ý rằng exprcallermembercác phương thức không hoàn toàn "đúng". Và ở đó bạn thấy một sự lặp lại của một nhận xét liên quan rằng sự phản chiếu nhanh hơn ~ 15 lần so với stacktrace.


9

EDIT: MethodBase có lẽ là một cách tốt hơn để chỉ lấy phương thức mà bạn tham gia (trái ngược với toàn bộ ngăn xếp cuộc gọi). Tuy nhiên, tôi vẫn lo ngại về nội tuyến.

Bạn có thể sử dụng StackTrace trong phương thức:

StackTrace st = new StackTrace(true);

Và nhìn vào các khung:

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

Tuy nhiên, hãy lưu ý rằng nếu phương thức được nội tuyến, bạn sẽ không ở trong phương thức mà bạn nghĩ là bạn. Bạn có thể sử dụng một thuộc tính để ngăn chặn nội tuyến:

[MethodImpl(MethodImplOptions.NoInlining)]

Nội tuyến do tối ưu hóa Phát hành đặc biệt khó khăn vì mã sẽ hoạt động khác nhau trong cấu hình Gỡ lỗi và Phát hành. Xem ra cho các tài sản nhỏ, họ là nạn nhân rất có thể của điều này.
ĐK.

Tôi tự hỏi tại sao bạn sử dụng new StackTrace(true)thay vì new StackTrace(false). Việc cài đặt đó truesẽ khiến dấu vết ngăn xếp bị khuất phục khi bắt giữ tên tệp, số dòng và vv, điều này có thể khiến cuộc gọi này chậm hơn. Mặt khác, một câu trả lời hay
Ivaylo Slavov

6

Cách đơn giản để đối phó là:

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

Nếu System.Reflection được bao gồm trong khối sử dụng:

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

4

Làm thế nào về điều này:

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

Thử cái này...

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

Tôi chỉ làm điều này với một lớp tĩnh đơn giản:

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

sau đó trong mã của bạn:

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.