2015-10-24 6 views
43

Używając Roslyn, chciałbym zmodyfikować mój kod C# przed rzeczywistą kompilacją. Na razie będę tylko coś takiego:Jak zmodyfikować kod przed kompilacją?

[MyAnotatedMethod] 
public void MyMethod() 
{ 
    // method-body 
} 

i na podstawie adnotacji, chciałbym wstrzyknąć trochę kodu na początku metody, a na koniec metody.

Jestem świadomy PostSharp, ale to nie jest to, co chciałbym.

Czy można to zrobić z Roslyn? A jeśli tak, czy możesz podać mi przykład?

+2

Jest też [Fody] (https://github.com/Fody/Fody) –

+2

Czy to nie jest to, co chcesz osiągnąć: http://www.sebbylive.com/articles/programming/aspect_oriented_programming_roslyn.aspx? Jego podstawowym pomysłem jest wykonanie modyfikacji i kompilacji za pomocą samodzielnego pliku wykonywalnego i dodanie tego exe jako narzędzia do kompilacji. – Tamas

+1

Lubię takie pytania, gdy z jednej strony masz legendę mówiącą [Nie] (https://social.msdn.microsoft.com/forums/vstudio/en-US/3593b4d9-c3f0-4e94-818a-7958930aeb4e/injecting -code-into-an-existing-assembly) i [quotes] (https://gist.github.com/SimonCropp/8485964) twierdząc, że zespół Roslyn powiedział, że będzie obsługiwał wtryskiwanie kodu, prawdopodobnie nie dla pierwszego release'. @ Bjørn-Roger Kringsjå, czy możesz wskazać, jak można oczekiwać, że Roslyn wdroży to ** w inny sposób ** do normalnych implementacji: http://www.codeproject.com/Articles/14334/AOP-using-System- Refleksja -Emit-Code-Injection-IL –

Odpowiedz

16

Oto szybki i brudny sposób robienia tego, co chcesz. Jest oparty na jednym z powyższych komentarzy, który wskazuje na SebbyLive. To tylko dowód koncepcji, nie próbowałbym jej używać w produkcji.

Podstawową ideą jest zmiana kompilatora projektu, który chcesz zmodyfikować. I ten zmieniony kompilator zrobi wstrzyknięcie kodu. Musisz więc napisać nowy kompilator (AopCompiler.exe) i ustawić go jako narzędzie do budowania w swoim projekcie.

Ustawianie AopCompiler.exe jako narzędzie budowania jest łatwe, w projekcji plik, trzeba by dodać następujące dwa wiersze:

<CscToolPath>$(SolutionDir)AopCompiler\bin\Debug</CscToolPath> 
<CscToolExe>AopCompiler.exe</CscToolExe> 

AopCompiler powinna być prosta aplikacja konsoli. To robi także modyfikację kodu i kompilację. Jeśli nie chcesz zmodyfikować kod źródłowy, tylko budować, to najprostszym sposobem jest wywołanie csc.exe siebie:

static void Main(string[] args) 
{ 
    var p = Process.Start(@"C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe", 
      string.Join(" ", args)); 
    p.WaitForExit(); 
} 

Więc jeśli to ustawić tak daleko, że masz normalny proces budowy, bez aspektu tkania.

W tym momencie, jeśli sprawdzisz, co jest w args, zobaczysz, że istnieje ścieżka pliku do pliku .RSP, który zawiera wszystkie parametry wiersza polecenia dla csc.exe. Oczywiście te parametry zawierają również wszystkie nazwy plików .CS. Więc możesz sparsować ten plik .RSP i znaleźć wszystkie pliki .CS, które są częścią kompilacji.

Przy pomocy plików C#, przepisanie można wykonać z Roslyn. Istnieje wiele samouczków na temat CSharpSyntaxRewriter, na przykład here i here. Musisz napisać niestandardową CSharpSyntaxRewriter, która sprawdza dany atrybut, a następnie dodać rejestrowanie do początku znalezionych metod. Dodawanie rejestrowania na końcu każdej metody jest nieco trudniejsze, ponieważ może istnieć wiele punktów wyjścia. Aby je znaleźć, możesz użyć analizy przepływu sterowania. Wbudowana analiza przepływu kontrolnego Roslyn może dać dokładnie to, czego szukasz, właściwość ExitPoints zawiera zestaw instrukcji wewnątrz regionu, który przeskakuje do lokalizacji poza regionem.

Aby uzyskać model semantyczny (a następnie wykonać analizę CFG) można zrobić coś takiego:

public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) 
{ 
    var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree); 
    // semanticModel.AnalyzeControlFlow(node.Block) 
    return node; 
} 

Wreszcie, aby przetworzyć każdy z plików wejściowych, twój AopCompiler, wystarczy po prostu zadzwonić metoda przeszukiwacza Visit w katalogu głównym drzewa. To wytworzy zmodyfikowane drzewo, które możesz zapisać do pliku. (Możesz zmodyfikować oryginalny plik lub zapisać wynik na nowym i odpowiednio zmienić plik .RSP.)

Przepraszamy za brak kompleksowego rozwiązania, ale mam nadzieję, że wystarczy, aby zacząć.

+0

Daje mi 'The -Element poniżej -Element is unknown". Jak widzisz, wstawiłem twoje dwa wiersze do mojego pliku '.csproj' na końcu wewnątrz elementu' project'. Co tu jest źle? – C4u

7

Jak wskazałem w moim komentarzu, nie jest on obecnie dostępny. Chociaż można umieścić coś razem za pomocą technik AOP pokazanych here z Roslyn Scripting API, aby zapewnić bardzo elastyczne rozwiązanie.

Prawidłowa odpowiedź w tym momencie jest taka, że ​​zespół Roslyn ma otwartą propozycję/problem, aby go wesprzeć. Dzięki Fundacji .Net i Microsoft uczestniczących w Open Source można przeczytać o tym tutaj: wyposażony rozwój

https://github.com/dotnet/roslyn/issues/5561

3

To jest trochę bardziej brudne niż przy użyciu adnotacji, ale kod blokowania za pomocą dyrektywy preprocesora pozwoli aby skonfigurować sposób kompilacji kodu na podstawie flag.

http://www.codeproject.com/Articles/304175/Preprocessor-Directives-in-Csharp

kodu w #ifMyFlag w poniżej zostaną połączone tylko wtedy, MyFlag definiuje

#define MyFlag 
public void MyMethod() 
{ 
    #if MyFlag 
     // Flag conditional code 

    #endif 

    // method-body 
} 
+0

Dobra odpowiedź - przypominam sobie, robiąc to na projekcie przed wiekami, używając statycznego wywołania funkcji w warunku If. To nie jest dużo więcej pracy niż Atrybut i znacznie łatwiejsze do wdrożenia niż podejście AOP –