通过T4代码生成实现自动INotifyPropertyChanged实现?
我目前正在设置我的一个新项目,并想知道如何实现我的ViewModel类具有INotifyPropertyChanged支持,同时不需要自己手动编码所有属性。通过T4代码生成实现自动INotifyPropertyChanged实现?
我看着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上发布我的想法,作为一个问题来获得一些反馈意见,也许有人建议如何做到更好/更容易/更安全。
Here's a great post by Colin Eberhardt通过使用EnvDTE直接从Visual Studio中检查自定义属性,从T4生成依赖属性。由于该文章包含浏览代码节点的简单实用方法,因此适应它来检查字段并适当地生成代码应该不难。
请注意,当从VS使用T4时,您不应该在自己的程序集上使用Reflection,否则它们会被锁定,您将不得不重新启动Visual Studio才能重建。
它应该肯定会工作。
我建议你先写一个实现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);
}
#>
等。
有很多方法来剥皮这只猫。
我们一直在用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>
是的,我已经考虑使用代码片段。然而,使用代码片段仍然是更多的工作,然后使用T4(当它终于工作..) 我看着PostSharp,但事情是,我只是不想在我的项目中有更多的依赖项。 – chrischu 2010-06-03 20:44:45
这是真的。但是,在我看来,我们在这种模式上花费了很多精力,只是因为我们讨厌它而且很丑。但它不难理解或维护。需要5秒钟才能添加属性和OnPropertyChanged样板。那么,有什么更好的2-3行重复代码或对PostSharp或一些钝的T4的依赖? – 2010-06-03 21:32:57
每行2至3行重复代码。这加起来。此外,使用OnPropertyChanged生成的代码 - 调用OnPropertyChanged(“SomeProperty”)对拼写错误不安全的问题会自动克服,而不会影响运行时性能(如通过反射)。 – chrischu 2010-06-15 17:07:09
我觉得自己像一个白痴,但我不知道T4是什么,直到我只是因为这个问题而使用它。我不敢相信这不是更多的讨论! – BFree 2010-06-03 18:27:00
我也是。我从一年前的C#预处理器线程(http://stackoverflow.com/questions/986404/does-a-c-preprocessing-tool-exist)得到了这里。卫生署。 – lo5 2010-06-04 09:30:17
另请参阅http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist – 2011-06-01 11:38:51