Страницы

Поиск по вопросам

понедельник, 30 декабря 2019 г.

Узнать, есть ли атрибут у вызывающего метода?

#c_sharp #attribute #атрибуты


Есть тестовое консольное приложение.

internal class Program
{
    [MyTest("TestDescription")]
    static void Main(string[] args)
    {
        TestSchmest ta = new TestSchmest();
        ta.GetSomeThingMethod();

        Console.ReadKey();
    }
}

internal class TestSchmest
{
    public void GetSomeThingMethod([CallerMemberName] string methodName = "")
    {
        Console.WriteLine("\t - In hell we die!!!");
        WhoCalledMe();
        Console.WriteLine();
        Console.WriteLine("Method \"GetSomeThingMethod\" was called by {0}.", methodName);

        // methodName.GetAttribute ???
    }

    private void WhoCalledMe([CallerMemberName] string methodName = "")
    {
        Console.WriteLine("\t - For what?!");
        Console.WriteLine();
        Console.WriteLine("Method \"WhoCalledMe\" was called by {0}.", methodName);

        // methodName.GetAttribute ???
    }
}

public class MyTestAttribute : Attribute
{
    protected string Description;

    public MyTestAttribute(string description)
    {
        Description = description;
    }
}


Сначала вызывается метод GetSomeThingMethod класса TestSchmest, который в свою очередь
вызывает метод WhoCalledMe. Можно ли как-то из методов класса TestSchmest узнать, указан
ли атрибут у вызывающего их метода или нет.

Т.е. ta.GetSomeThingMethod() должен показать, что атрибут есть, а WhoCalledMe() что
его нет.

Название класса, в котором вызываются методы (в данном случае Program) может быть
неизвестно.
    


Ответы

Ответ 1



С помощью класса StackTrace можно получить предыдущий кадр стека, относящийся к вызвавшему методу. Атрибут CallerMemberName при этом не требуется. public void GetSomeThingMethod() { string methodName = ""; MyTestAttribute attribute = null; var st = new StackTrace(); if (st.FrameCount > 1) { var prevFrame = st.GetFrame(1); // получаем кадр стека для вызвавшего метода var caller = prevFrame.GetMethod(); // получаем сам вызвавший метод methodName = caller.Name; var attributes = caller.GetCustomAttributes(typeof(MyTestAttribute), false); if (attributes.Length > 0) attribute = (MyTestAttribute)attributes[0]; } Console.WriteLine("\t - In hell we die!!!"); WhoCalledMe(); Console.WriteLine(); Console.WriteLine("Method \"GetSomeThingMethod\" was called by {0}.", methodName); if (attribute != null) Console.WriteLine("Attribute is present."); } Результат: Method "GetSomeThingMethod" was called by Main. Attribute is present.

Ответ 2



Можно получить MethodInfo через класс StackTrace. Учтите, что это достаточно медленный способ, к тому же вам придется отключить inlining, чтобы JIT не вздумал встроить вызываемый / вызывающий метод в тело другого метода - иначе вы просто не увидите метод в stack trace: using System; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; namespace ConsoleApplication58 { class Program { static void Main(string[] args) { AttributedMethod(); NonAttributedMethod(); } [MyTest("Some Description")] [MethodImpl(MethodImplOptions.NoInlining)] static void AttributedMethod() { CallerDetectionSample(); } [MethodImpl(MethodImplOptions.NoInlining)] static void NonAttributedMethod() { CallerDetectionSample(); } [MethodImpl(MethodImplOptions.NoInlining)] public static void CallerDetectionSample() { // Get call stack StackTrace stackTrace = new StackTrace(); // Get calling method name var attibutes = stackTrace.GetFrame(1).GetMethod().GetCustomAttributes(typeof(MyTestAttribute), true); var description = attibutes.Cast() .Select(a => a.Description) .FirstOrDefault() ?? "no attribute!"; Console.WriteLine(description); } } public class MyTestAttribute : Attribute { public string Description { get; set; } public MyTestAttribute(string description) { Description = description; } } }

Комментариев нет:

Отправить комментарий