2012-10-29 10 views
8

Mam dość skomplikowaną rzecz wypchaną do szablonu T4. Zasadniczo biorę coś takiegoCzy jest coś, co sprawi, że kod T4 będzie bardziej ... czysty?

{= foo =} więcej tekstu ...

i przekształcić go w klasie (Widok) tak:

public class MyView 
{ 
    public string foo{get;set;} 
    public string Write() 
    { 
    return [email protected]" more text..."; 
    } 
} 

Kod generowany jest oczywiście znacznie bardziej skomplikowane. W każdym razie szablon T4 zawiera obecnie ponad 600 linii kodu i naprawdę szybko staje się niemożliwy do zarządzania. Uważam, że głównym problemem jest mieszanie kodu i "treści" (tj. Kodu statycznego). Naprawdę nie wiem, jak rozwiązać ten problem (choć oczywiście nie wpływa to na generowany kod). Nie widzę też żadnego możliwego sposobu testowania mojego kodu T4, poza testowaniem go pod kątem błędów wykonania T4. Oczywiście wydaje się, że prawie niemożliwe jest przetestowanie wygenerowanego kodu.

Czy istnieją ramy lub techniki typu "widok modelu", których mogę użyć, aby mój kod szablonu T4 był bardziej czysty?

+0

To może nie być opcja, ale ja wolę szablony Resharpera (http://www.jetbrains.com/resharper/features/code_templates.html) niż szablony T4. Ponadto, [Resharper] (http://www.jetbrains.com/resharper/) jest niesamowitym narzędziem do innych zastosowań. Warto kosztów. – TylerOhlsen

+2

Resharper jest dobrym narzędziem, ale IMHO porównujące szablony Resharper z T4 porównuje jabłka i pomarańcze. Szablony Resharper promują anty-wzór "kopiuj-wklej" dzięki obsłudze narzędzi, co prowadzi do coraz więcej zbędnego kodu, który trzeba utrzymywać, zwiększając w ten sposób koszty utrzymania. T4 (i inne narzędzia) minimalizuje redundancję polegającą na tym, że piszesz meta-program (który ma niską redundancję), który generuje artefakty kodu (z dużą ilością redudancji). Kluczem jest to, że zwykłe połączenie meta-programu z wygenerowanym kodem nie zostanie utracone. – FuleSnabel

Odpowiedz

1

Po długiej podróży w końcu sprawdziłem pierwsze testy jednostkowe do moich szablonów T4. Zasadniczo, zrobiłem abstrakcję "widoku" (rzeczywisty szablon T4) i "logikę" (co generuje kod, ale tak naprawdę nie wyprowadza go i nie polega na T4).

I następnie zrobił to o krok dalej i użył dużego hack, aby mój plik logiczny był kompilowany poza T4. Miało to dobry wpływ na to, aby działało intellisense i błędy kompilatora. Pozwalają mi również uzyskać dostęp do mojej klasy logicznej z testów jednostkowych, odwołując się do projektu.

Napisałem artykuł z przykładami kodu (przed/po) i przykładowymi testami jednostkowymi on my blog, jeśli chcesz mieć długie szczegóły, jak to zrobić.

5

IMHO dwa najważniejsze pojęcia pisząc skomplikowanych szablonów jest

  1. Oddzielenie modelu i widoku - Ułatwia utrzymywanie logiki szablonu do minimum (często przyczyną problemu technicznego). Osobiście uważam, że nie wymaga to ram, wymaga to tylko tego.
  2. Częściowo jest twoim przyjacielem - Używam generalnie T4 do generowania kodu szkieletu z modelu. Konkretne zachowanie może nie być warte wysiłku, aby wprowadzić model, lepiej często pozwalać, aby takie zachowanie pojawiło się poprzez użycie częściowych klas lub metod.

Nie tak ważne, ale ładny

  1. uczynić kod przeszukiwać - nie polegać na T4 Addons ponieważ uważam, żaden z nich na tyle dobra, z braku IntelliSense aby nawigować kod, który muszę wprowadzić do przeszukiwania kodu. Może być tak proste, jak zamiast wywoływania właściwości Column Name, nazywam ją ColumnName.
  2. Wstawianie "znaczników" na wyjściu - Ułatwia znajdowanie kodu, który wygenerował tę część danych wyjściowych.

Przykład oddzielenie modelu/Wygląd:

<# 
    // Model for Dependency Pooperties 
    Model = new [] 
    { 
     new ClassDefinition ("MyDataGrid") 
      { 
       P ("CultureInfo"   , "CultureInfo"), 
       P ("Pen"     , "CellBorderPen"), 
       P ("IEnumerable<object>" , "Rows"), 
       C ("WColumnDefinition"  , "Columns"), 
      }, 
    }; 
#> 
// Include the view 
<#@ include file="..\T4\DependencyProperties.ttinclude" #> 

Widok następnie przechodzi nad i tworzenia modelu właściwości zależności.

Zachowanie właściwości uzależniających są następnie realizowane jako metody częściowych

partial void Changed_Columns(
     ObservableCollection<WColumnDefinition> oldValue, 
     ObservableCollection<WColumnDefinition> newValue 
     ) 
    { 
     HookUpColumns(oldValue, null); 
     HookUpColumns(newValue, this);    
    } 

Należy zauważyć, że wprowadzenie tego specyficzne zachowanie w modelu znacznie komplikuje modelu.

Wreszcie; Potrzeba czasu, aby kompetentny programista mógł kompetentnie pisać metaprogramy.Zajęło mi kilka prób, zanim dotarłem do stylu, który uważam za możliwy do utrzymania, ale dla mnie było to warte wysiłku, ponieważ jestem w stanie szybciej przesyłać jakość.

Mam nadzieję, że to pomoże ...

PS. Nie sądzę, by ktokolwiek twierdził, że T4 jest zawsze elegancki, ale mimo to jest to przydatne.

+0

Właściwie rozważałem oddzielenie "modelu" od zwykłego zespołu i po prostu posiadanie uproszczonego "widoku" T4, który faktycznie obsługuje generowanie kodu, a następnie połączenie kodu T4 z regularnym zestawem. To rozwiązuje również większość powikłania testów jednostkowych – Earlz

+0

Ok, myślę, że znalazłem "dobry" sposób na przynajmniej przeprowadzenie testów jednostkowych, co również zmusza do ścisłego oddzielenia "widoku" od "modelu". Opublikuję tutaj, gdy wszystkie szczegóły zostaną uporządkowane, ale w zasadzie dotyczy to "modelu" w innym pliku '.cs', który zostanie włączony do wygenerowanego wyjścia T4. To nie jest perfekcyjne, ale jest to najlepsze, jakie udało mi się znaleźć do tej pory. – Earlz

+1

Możesz chcieć spojrzeć na moją [własną odpowiedź] (http://stackoverflow.com/a/13486081/69742), bo w końcu to zrobiłem, tworzenie intellisense, błędy kompilatora i testy jednostkowe działają na logikę używaną przez mój szablon T4 – Earlz

Powiązane problemy