2013-04-13 12 views
9

Ktoś zadał mi pytanie, w jaki sposób możemy drukowaćC# atrybut nad głównym

line no 1 
line no 2 
line no 3 

bez zmiany główną metodę, która czyta

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

Teraz jedno podejście było mieć wiele punktów wejścia dla Aplikacja konsoli. Jednak próbowałem innego podejścia, które wygląda następująco:

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

Kiedy zastosować pułapkę na każdym z WriteLine, jestem w stanie zobaczyć, że podejście działa, jednak sam nie znajduje odzwierciedlenie na konsola.

Po prostu ciekawy.

+1

'Bez zmiany głównej metody, która brzmi:" naprawdę?!? "? –

+2

@MichaelPerrenoud: Jestem pewien, że jest to ćwiczenie, które ma zająć pewne sprytne myślenie. Szczerze mówiąc uważam, że to naprawdę interesujące. –

+0

@ArnoSluismans, w porządku. I to naprawdę proste, to było naprawdę dziwne. Ale dzięki za skierowanie mnie właściwą drogą, spójrz na moją odpowiedź. –

Odpowiedz

4

Wykorzystajmy AOP i wykorzystajmy PostSharp. Będziesz potrzebował download and install it first, a następnie będziesz musiał dodać do niego odnośnik za pomocą NuGet. Musisz go zainstalować, ponieważ jest to hak do kompilatora. Zobacz, kiedy skompilujesz swój kod, PostSharp faktycznie wstrzyknie IL do wyjścia na podstawie haków, których używasz.

Po wykonaniu tych dwóch rzeczy dodać nową klasę atrybutu 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"); 
     } 
    } 
} 

a następnie zmodyfikować metodę Main tak:

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

jesteś problem może być z podziałem na wyszukiwanie haków, które są wyzwalane przed i po wykonaniu metody Main aplikacji konsoli.

  • Pierwszy hak jest Program statyczny konstruktor, który jest guarantee wykonać przedMain metoda Program klasę.

  • drugie jest wydarzeniem ProcessExit z AppDomain, który „Występuje, gdy wychodzi proces-rodzic domeny domyślnej aplikacji” . Możesz użyć konstruktora statycznego, aby zasubskrybować to wydarzenie.


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

drukuje:

line no 1 
line no 2 
line no 3 

następna część będzie dość długa. Spróbuję wyjaśnić, jaki jest problem z SomeAttribute w twoim pytaniu.

Przede wszystkim rozważ to pytanie StackOverflow, aby dokładnie wiedzieć, when custom attributes constructors are executed. To nie jest takie proste, jak mogłoby się wydawać na pierwszy rzut oka.

Jak już wiemy, ctor niestandardowego atrybutu zostanie wykonany dopiero wtedy, gdy uzyskasz do niego dostęp za pomocą refleksji. Więc na przykład proste wykonanie programu nie wywoła konstruktora atrybutów.Ale dlaczego twój punkt przełomowy trafił, gdy zastosujesz metodę SomeAttribute do Main? Okazuje się, że studio wizualne używa refleksu, aby znaleźć główną metodę i dołączyć do aplikacji debugger. Ale w tym momencie nie ma okna konsoli. Tak więc oświadczenie Console.WriteLine jest bezużyteczne i daje efekt. Co więcej, wydaje się, że wszystkie następne wyciągi są blokowane na wyjściu konsoli.

Więc następny kod będzie produkować różne wyniki, w zależności czy go uruchomić z VS debugger lub nie:

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

    } 
} 

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

Jeśli uruchomić go bez debuggera (Ctrl + F5 w domyślnej konfiguracji VS), zobaczysz, że program się kończy i nie pojawiają się żadne okna. Po uruchomieniu go z debuggera (F5) zobaczysz

enter image description here

i bez okna konsoli obok VS tylko wygrać formularzy ikony: enter image description here

Jak już wspomniano wcześniej, podczas próby zapisu na konsolę, gdy nie ma nikogo, wszystkie pozostałe połączenia z numerem Console.WriteLine nie mają wpływu na działanie aplikacji konsoli. Dlatego możesz zobaczyć wszystkie komunikaty konsoli, nawet jeśli w konstruktorze ustawisz punkt przerwania.

+1

+1 dla innego sprytnego rozwiązania! –

+0

+1 za odpowiedź, ale starałem się dowiedzieć, dlaczego moje rozwiązanie nie działa. – user2277247

+0

@ user2277247 Zaktualizowałem swoją odpowiedź z wyjaśnieniami do zaobserwowanego zachowania –

6

Myślę, że Ilya Ivanov's answer jest prawdopodobnie najlepszy. Jednak uważaj moją również za zabawną odpowiedź:

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"); 
    } 
} 
+0

+1 za myślenie po wyjęciu z pudełka. Nie ma potrzeby wykonywania metody 'Main', ładnego rogu w instrukcji problemu. –

Powiązane problemy