Автоматическая Реализация INotifyPropertyChanged посредством генерации кода T4?

Я в настоящее время работаю над установкой нового моего проекта и задавался вопросом, как я мог достигнуть того своего ViewModel, который классы сделали, чтобы INotifyPropertyChanged поддерживал, не имея необходимость к handcode все свойства сам.

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

Таким образом, я думал о генерации реализаций свойства с T4.

Установка была бы этим: у Меня есть класс ViewModel, который объявляет просто его фоновые переменные Свойств, и затем я использую 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 как вопрос получить некоторую обратную связь на нем и возможно некоторый совет, как это может быть сделано лучше/легче/более безопасное.

10
задан chrischu 3 June 2010 в 17:39
поделиться

3 ответа

Вот отличная публикация Колина Эберхардта о создании свойств зависимостей из T4 путем проверки пользовательских атрибутов непосредственно из Visual Studio с помощью EnvDTE. Не должно быть сложно адаптировать его для проверки полей и соответствующего создания кода, поскольку пост содержит простые служебные методы для просмотра узлов кода.

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

7
ответ дан 4 December 2019 в 00:59
поделиться

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

Мы использовали 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>
3
ответ дан 4 December 2019 в 00:59
поделиться

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

Я бы порекомендовал сначала написать базовый класс, реализующий 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);
    }
#>

И так далее.

1
ответ дан 4 December 2019 в 00:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: