2010-06-03 11 views
10

Attualmente sto lavorando alla creazione di un mio nuovo progetto e mi chiedevo come avrei potuto ottenere che le mie classi ViewModel avessero il supporto di INotifyPropertyChanged pur non avendo a portata di mano tutte le proprietà.Automatic InotifyPropertyChanged Implementazione tramite generazione di codice T4?

Ho esaminato i framework AOP, ma penso che avrebbero solo fatto saltare il mio progetto con un'altra dipendenza.

Quindi ho pensato di generare le implementazioni di proprietà con T4.

L'installazione sarebbe questa: ho una classe ViewModel che dichiara solo le sue variabili di background Proprietà e quindi utilizzo T4 per generare le implementazioni di proprietà da esso.

Per esempio, questo sarebbe il mio ViewModel:

public partial class ViewModel 
{ 
    private string p_SomeProperty; 
} 

Poi T4 sarebbe andare oltre il file di origine e cercare per le dichiarazioni membro denominato "p_" e generare un file in questo modo:

public partial class ViewModel 
{ 
    public string SomeProperty 
    { 
     get 
     { 
      return p_SomeProperty; 
     } 
     set 
     { 
      p_SomeProperty= value; 
      NotifyPropertyChanged("SomeProperty"); 
     } 
    } 
} 

Questo approccio ha alcuni vantaggi ma non sono sicuro che possa funzionare davvero. Quindi ho voluto postare la mia idea qui su StackOverflow come una domanda per avere un feedback su di esso e forse qualche consiglio su come può essere fatto meglio/più facile/più sicuro.

+2

Mi sento un deficiente, ma non avevo idea di cosa fosse T4 fino a quando ho cercato su Google solo per questa domanda. Non posso credere che questo non sia più parlato! – BFree

+0

Anch'io. Sono arrivato dal vecchio thread del preprocessore C# (http://stackoverflow.com/questions/986404/does-a-c-preprocessing-tool -exist). Doh. – lo5

+0

vedere anche http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist –

risposta

7

Here's a great post by Colin Eberhardt sulla generazione di proprietà di dipendenza da un T4 mediante l'ispezione di attributi personalizzati direttamente da Visual Studio con EnvDTE. Non dovrebbe essere difficile adattarlo per ispezionare i campi e generare codice in modo appropriato poiché il post contiene semplici metodi di utilità per esplorare i nodi del codice.

Si noti che quando si utilizza T4 da VS, non è necessario utilizzare Reflection sui propri assiemi o verranno bloccati e sarà necessario riavviare Visual Studio per ricostruire.

+0

Questo era dolce. Ho adattato il modello per fare la stessa cosa con la generazione di descrittori di tipi personalizzati. Salvato letteralmente migliaia di righe di codice. – Will

+1

Si noti che a partire da VS2010 SP1, il problema di blocco è stato risolto, quindi la riflessione ora è OK da usare. – GarethJ

1

Dovrebbe funzionare sicuramente.

mi consiglia prima scrittura di una classe base che implementa INotifyPropertyChanged, dandogli un metodo protected void OnPropertyChanged(string propertyName), rendendo memorizzare nella cache i suoi PropertyChangeEventArgs oggetti (uno per ogni nome di proprietà unica - nessun punto nella creazione di un nuovo oggetto ogni volta che l'evento viene generato) e la tua classe generata da T4 deriva da questa base.

per ottenere i membri che hanno bisogno di proprietà implementate, si può solo fare qualcosa di simile:

BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; 
FieldInfo[] fieldsNeedingProperties = inputType.GetFields(flags) 
    .Where(f => f.Name.StartsWith("p_")) 
    .ToArray(); 

e passare da lì:

<# foreach (var field in fieldsNeedingProperties) { #> 
<# string propertyName = GetPropertyName(field.Name); #> 
    public <#= field.FieldType.FullName #> <#= propertyName #> { 
     get { return <#= field.Name #>; } 
     set { 
      <#= field.Name #> = value; 
      OnPropertyChanged("<#= propertyName #>"); 
     } 
    } 
<# } #> 

<#+ 
    private string GetPropertyName(string fieldName) { 
     return fieldName.Substring(2, fieldName.Length - 2); 
    } 
#> 

E così via.

+0

Ben riflessione non è davvero un'opzione (vedere la risposta di Julien Lebosquain). – chrischu

+1

@chrischu: Se lo dici tu. Mi sembra che escludere completamente la riflessione sia un po 'drastico per qualcosa che deve essere fatto solo una volta per classe. Ho usato questa tecnica molte volte prima di scrivere lezioni per me quando non avevo voglia di scrivere così tanto. Quindi devi riavviare Visual Studio in seguito. È così terribile? –

+1

Si noti che a partire da VS2010 SP1, il problema di blocco è stato risolto. – GarethJ

3

Ci sono molti modi per scuoiare questo gatto.

Abbiamo iniziato a collaborare con PostSharp per iniettare la piastra di stampa INotifyProperty. Sembra funzionare abbastanza bene.

Detto questo, non c'è motivo per cui T4 non funzionerebbe.

Sono d'accordo con Dan sul fatto che si dovrebbe creare l'implementazione della classe base di OnPropertyChanged.

Hai considerato l'utilizzo di uno snippet di codice?Scriverà il boilerplate per te. L'unico svantaggio è che non si aggiornerà automaticamente se si desidera modificare il nome della proprietà in un secondo momento.

<?xml version="1.0" encoding="utf-8" ?> 
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
    <CodeSnippet Format="1.0.0"> 
    <Header> 
     <Title>propin</Title> 
     <Shortcut>propin</Shortcut> 
     <Description>Code snippet for property and backing field with support for INotifyProperty</Description> 
     <SnippetTypes> 
     <SnippetType>Expansion</SnippetType> 
     </SnippetTypes> 
    </Header> 
    <Snippet> 
     <Declarations> 
     <Literal> 
      <ID>type</ID> 
      <ToolTip>Property type</ToolTip> 
      <Default>int</Default> 
     </Literal> 
     <Literal> 
      <ID>property</ID> 
      <ToolTip>Property name</ToolTip> 
      <Default>MyProperty</Default> 
     </Literal> 
     </Declarations> 
     <Code Language="csharp"> 
     <![CDATA[private $type$ _$property$; 

    public $type$ $property$ 
    { 
     get { return _$property$;} 
     set 
    { 
     if (value != _$property$) 
     { 
     _$property$ = value; 
     OnPropertyChanged("$property$"); 
     } 
    } 
    } 
    $end$]]> 
     </Code> 
    </Snippet> 
    </CodeSnippet> 
</CodeSnippets> 
+0

Sì, ho preso in considerazione l'utilizzo di uno snippet di codice. Comunque usare uno snippet di codice è ancora molto più utile dell'uso di T4 (quando finalmente funziona ..) Ho visto PostSharp ma il fatto è che non voglio avere più dipendenze nel mio progetto. – chrischu

+0

È vero. Ma, a mio parere, spendiamo molte energie su questo schema, solo perché lo odiamo ed è brutto. Ma non è difficile da capire o da mantenere. Sono necessari 5 secondi per aggiungere una proprietà e il codice di caldaia OnPropertyChanged. Quindi, cosa c'è di meglio 2-3 righe di codice ripetitivo o una dipendenza da PostSharp o qualche T4 ottuso? –

+0

2-3 righe di codice ripetitivo per proprietà. Questo aggiunge. Inoltre con il codice generato che rende OnPropertyChanged-chiama il problema che OnPropertyChanged ("SomeProperty") non è sicuro per errori di ortografia viene automaticamente superato senza richiedere un impatto sulle prestazioni di runtime (come attraverso il riflesso). – chrischu

Problemi correlati