Атрибут c # поверх основного

Кто-то спросил меня, как мы можем печатать

line no 1
line no 2
line no 3

Без изменения основного метода, который читает

static void Main(string[] args)
{
    Console.WriteLine("line no 2");
}

Теперь один из подходов заключался в наличии нескольких точек входа для консольного приложения. Однако я попробовал другой подход, который выглядит следующим образом:

class Program
{
    [Some]
    static void Main(string[] args)
    {
        Console.WriteLine("line no 2");
    }
}
class SomeAttribute : Attribute
{
    public SomeAttribute()
    {
        Console.WriteLine("line no 1");
    }
    ~SomeAttribute()
    {
        Console.WriteLine("line no 3");
    }
}

Когда я применяю точку останова к каждой из WriteLine, я вижу, что подход работает, однако это не отражается на консоли.

Просто любопытно.


person user2277247    schedule 13.04.2013    source источник
comment
Without changing a main method which reads ... правда?!?   -  person Mike Perrenoud    schedule 13.04.2013
comment
@MichaelPerrenoud: Я почти уверен, что это упражнение требует некоторого умного мышления. Я, честно говоря, считаю, что это довольно интересно.   -  person Lee White    schedule 13.04.2013
comment
@ArnoSluismans, все в порядке. И это действительно просто, это было действительно странно. Но спасибо, что направили меня по правильному пути, взгляните на мой ответ.   -  person Mike Perrenoud    schedule 13.04.2013
comment
Все приведенные ниже решения полезны, но мне любопытно, почему, несмотря на достижение контрольных точек, как и ожидалось, метод атрибутов не работает.   -  person user2277247    schedule 13.04.2013


Ответы (3)


Проблема может быть разбита на поиск перехватчиков, которые срабатывают до и после Main выполнения метода консольного приложения.

  • Первая ловушка - это статический конструктор Program, который является гарантия выполнения раньше Main метода в Program классе.

  • Во-вторых, событие ProcessExit для AppDomain, которое «Происходит при выходе из родительского процесса домена приложения по умолчанию». Вы можете использовать статический конструктор, чтобы подписаться на это событие.


class Program
{
    static Program()
    {
        Console.WriteLine("line no 1");

        AppDomain.CurrentDomain.ProcessExit += 
                                          (s, a) => Console.WriteLine("line no 3");
    }

    static void Main(string[] args)
    {
        Console.WriteLine("line no 2");
    }
}

печатает:

line no 1
line no 2
line no 3

Следующая часть будет довольно длинной. Постараюсь объяснить, в чем проблема с SomeAttribute в вашем вопросе.

Прежде всего рассмотрите этот вопрос StackOverflow, чтобы точно знать , когда выполняются конструкторы настраиваемых атрибутов < / а>. Это не так просто, как может показаться на первый взгляд.

Как мы уже знаем, ctor настраиваемого атрибута будет выполняться только тогда, когда вы получите к нему доступ через отражение. Итак, в вашем примере выполнение простой программы не запускает конструктор атрибутов. Но почему срабатывает ваша точка останова, когда вы применяете метод SomeAttribute к Main? Оказывается, Visual Studio использует рефлексию, чтобы узнать основной метод и прикрепить отладчик к вашему приложению. Но в этот момент окна консоли нет. Таким образом, утверждение Console.WriteLine бесполезно и производит эффект. Более того, кажется, что он блокирует все последующие операторы вывода на консоль.

Итак, следующий код будет давать разные результаты, в зависимости от того, запускаете ли вы его с отладчиком VS или нет:

class Program
{
    [MyAttribute]
    static void Main()
    {

    }
}

class MyAttribute : Attribute
{
    public MyAttribute()
    {
        MessageBox.Show("MyAttribute ctor");
    } 
}

Если вы запустите его без отладчика (Ctrl + F5 в конфигурации VS по умолчанию), вы увидите, что программа завершает работу и окна не появляются. Когда вы запустите его с помощью отладчика (F5), вы увидите

введите описание изображения здесь

и нет окна консоли рядом с VS, только значок формы выигрыша: введите описание изображения здесь

Как я описывал ранее, когда вы пытаетесь писать в консоль, когда никого нет, все остальные вызовы Console.WriteLine не влияют на ваше консольное приложение. Вот почему вы можете видеть любые сообщения консоли, даже если вы установили точку останова в конструкторе.

person Ilya Ivanov    schedule 13.04.2013
comment
+1 за ответ, но я пытался понять, почему мое решение не сработало. - person user2277247; 22.04.2013
comment
@ user2277247 Я обновил свой ответ, добавив объяснения вашего наблюдаемого поведения - person Ilya Ivanov; 22.04.2013

Я думаю, что ответ Ильи Иванова, возможно, лучший. Однако считайте и мой ответ забавным:

public class Program
{
    static Program()
    {
        Console.WriteLine("line no 1");
        Console.WriteLine("line no 2");
        Console.WriteLine("line no 3");
        Environment.Exit(0);
    }

    static void Main(string[] args)
    {
        Console.WriteLine("line no 2");
    }
}
person Sina Iravanian    schedule 13.04.2013
comment
+1 за нестандартное мышление. Нет необходимости выполнять метод Main, красивый угловой случай внутри постановки задачи. - person Ilya Ivanov; 29.10.2013

Давайте использовать АОП, и давайте использовать PostSharp. Вам нужно будет сначала загрузить и установить его, а затем после этого вам нужно будет добавить ссылка на него с помощью NuGet. Вы должны установить его, потому что это перехватчик компилятора. Видите ли, когда вы компилируете свой код, PostSharp фактически вставляет IL в вывод на основе используемых вами ловушек.

После того, как вы сделали эти две вещи, добавьте новый класс для атрибута AOP:

using PostSharp.Aspects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    [Serializable]
    public class ConsoleAspect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            base.OnEntry(args);

            Console.WriteLine("line no 1");
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            base.OnExit(args);

            Console.WriteLine("line no 3");
        }
    }
}

а затем измените свой Main метод следующим образом:

[ConsoleAspect]
static void Main(string[] args)
{
    Console.WriteLine("line no 2");
}
person Mike Perrenoud    schedule 13.04.2013