2010-06-03 2 views
10

В настоящее время я работаю над созданием нового проекта и задаюсь вопросом, как я могу добиться того, что у моих классов ViewModel есть поддержка INotifyPropertyChanged, не имея необходимости вручную вводить все свойства.Автоматическая реализация INotifyPropertyChanged посредством генерации кода T4?

Я просмотрел рамки AOP, но я думаю, что они просто взорвут мой проект другой зависимостью.

Так что я думал о создании реализаций свойств с помощью T4.

Настройка будет такой: у меня есть класс ViewModel, который объявляет только свои фоновые переменные Properties, а затем я использую T4 для создания из него реализации свойств.

Например, это будет мой ViewModel:

public partial class ViewModel 
{ 
    private string p_SomeProperty; 
} 

Тогда T4 будет идти поверх исходного файла и искать деклараций членов, названных «p_» и создать файл так:

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

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

+2

Я чувствую себя придурком, но я понятия не имел, что такое Т4, пока я просто не искал его из-за этого вопроса. Не могу поверить, что об этом больше не говорят! – BFree

+0

Я тоже. Я попал сюда из годового препроцессора C# (http://stackoverflow.com/questions/986404/does-a-c-preprocessing-tool-exist). Doh. – lo5

+0

см. Также http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist –

ответ

7

Here's a great post by Colin Eberhardt при создании свойств зависимостей от T4 путем проверки пользовательских атрибутов непосредственно из Visual Studio с помощью EnvDTE. Нетрудно адаптировать его для проверки полей и генерации кода соответствующим образом, поскольку сообщение содержит простые методы утилиты для просмотра узлов кода.

Обратите внимание, что при использовании T4 от VS вы не должны использовать Reflection на своих собственных сборках, иначе они будут заблокированы, и вам придется перезапустить Visual Studio, чтобы перестроить.

+0

Это было мило. Я адаптировал шаблон, чтобы сделать то же самое с созданием дескрипторов пользовательского типа. Сохранено буквально тысячи строк кода. – Will

+1

Обратите внимание, что с VS2010 SP1 проблема блокировки устранена, поэтому отражение теперь доступно для использования. – GarethJ

1

Это должно определенно работать.

я рекомендовал бы первым не писать базовый класс, который реализует INotifyPropertyChanged, придавая ему метод protected void OnPropertyChanged(string propertyName), что делает его кэшировать PropertyChangeEventArgs объектов (по одному на уникальное имя свойства - нет смысла в создании нового объекта каждый раз, когда событие происходит), и ваш T4-генерируемый класс вытекает из этой базы.

Чтобы получить элементы, которые необходимы свойства реализованы, вы можете просто сделать что-то вроде:

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

И идти оттуда:

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

И так далее.

+0

Отражение на самом деле не вариант (см. Ответ Жюльена Лебоскана). – chrischu

+1

@chrischu: Если вы так говорите. Мне кажется, что полное исключение отражения немного немного для того, что нужно сделать только один раз в классе. Я использовал эту технику много раз, прежде чем писать классы для меня, когда мне не хотелось делать так много набрав. Таким образом, вы должны перезапустить Visual Studio впоследствии. Это так страшно? –

+1

Обратите внимание, что с VS2010 SP1 проблема блокировки устранена. – GarethJ

3

Есть много способов кожи этой кошки.

Мы играли с PostSharp для ввода шаблона INotifyProperty. Кажется, это работает очень хорошо.

Это, как говорится, нет причин, по которым T4 не будет работать.

Я согласен с Дэном в том, что вы должны создать реализацию базового класса OnPropertyChanged.

Вы считали, что используете только фрагмент кода?Он напишет шаблон для вас. Единственный недостаток заключается в том, что он не будет обновляться автоматически, если вы хотите изменить имя свойства в какой-то более поздний срок.

<?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

Да, я рассмотрел использование фрагмента кода. Однако использование фрагмента кода по-прежнему намного больше, чем использование T4 (когда оно наконец работает ..) Я смотрел PostSharp, но дело в том, что я просто не хочу иметь никаких зависимостей в моем проекте. – chrischu

+0

Это правда. Но, на мой взгляд, мы тратим много энергии на этот шаблон, просто потому, что мы его ненавидим, и это уродливо. Но это не трудно понять или сохранить. Для добавления свойства и шаблона OnPropertyChanged требуется 5 секунд. Итак, что лучше 2-3 строк повторяющегося кода или зависимости от PostSharp или некоторого тупого T4? –

+0

2-3 строки повторяющегося кода на каждое свойство. Это добавляет. Кроме того, сгенерированный код, который делает OnPropertyChanged-вызовом, проблема, которую OnPropertyChanged («SomeProperty») небезопасна для орфографических ошибок, автоматически преодолевается без потери производительности во время выполнения (например, путем отражения). – chrischu

Смежные вопросы