通过T4代码生成自动INotifyPropertyChanged实现?

时间:2010-06-03 17:39:00

标签: c# t4 inotifypropertychanged

我目前正在设置我的新项目,并且想知道如何实现我的ViewModel类确实具有INotifyPropertyChanged支持,而不必亲自手动编码所有属性。

我查看了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上发布我的想法作为一个问题,以获得一些反馈,也许一些建议如何更好/更容易/更安全。

3 个答案:

答案 0 :(得分:7)

Here's a great post by Colin Eberhardt通过使用EnvDTE直接从Visual Studio检查自定义属性,从T4生成依赖项属性。由于帖子包含简单的实用程序方法来浏览代码节点,因此将其调整为检查字段并适当生成代码应该不难。

请注意,在VS中使用T4时,不应在自己的程序集上使用Reflection,否则它们将被锁定,您必须重新启动Visual Studio才能重建。

答案 1 :(得分:3)

有很多方法可以给这只猫上皮。

我们一直在用PostSharp来注入INotifyProperty样板。这似乎工作得很好。

话虽如此,T4没有理由不起作用。

我同意Dan的观点,你应该创建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>

答案 2 :(得分: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);
    }
#>

等等。

相关问题