C# 编写MVVM样板代码的更好方法?

C# 编写MVVM样板代码的更好方法?,c#,wpf,silverlight,mvvm,inotifypropertychanged,C#,Wpf,Silverlight,Mvvm,Inotifypropertychanged,我发现自己最近写了很多样板MVVM代码,想知道是否有一种奇特的方法可以避免编写这些代码?我已经使用了一个ViewModelBase类,该类实现了INotifyPropertyChanged,但它并不能解决必须编写所有访问器代码等问题。可能是通过编写一个自定义属性来实现,还是通过模板系统 public MyClass : ViewModelBase { private int someVariable; public int SomeVariable {

我发现自己最近写了很多样板MVVM代码,想知道是否有一种奇特的方法可以避免编写这些代码?我已经使用了一个
ViewModelBase
类,该类实现了
INotifyPropertyChanged
,但它并不能解决必须编写所有访问器代码等问题。可能是通过编写一个自定义属性来实现,还是通过模板系统

public MyClass : ViewModelBase
{
    private int someVariable;

    public int SomeVariable
    {
        get
        {
            return this.someVariable;
        }

        set
        {
            this.someVariable = value;
            this.NotifyPropertyChanged("SomeVariable");
        }
    }
}

面向方面编程(AOP)是减少此类样板代码数量的一种方法。一个广为人知的框架是。还有一个免费的快速版。
您可以使用属性(直接在类上,或者作为对代码中满足特定条件集的所有点的多播)来标记代码应该集成的位置,并在构建期间在实现中进行PostSharp编织。您可以找到INotifyPropertyChanged实现的示例。

基于AOP的方法(无论您使用哪个框架)的优点是,您可以在以后更改实现,并且这些更改会反映在现有的代码库中。还可以将方面应用于大量现有的类

哇。。。在评论中有很多答案,而在答案中没有那么多。作为可爱的新属性的替代,如何?我有很多这样的接口,只需单击鼠标按钮,就可以完全实现我的所有接口(自定义和.NET)

使用宏的缺点:

您可以使用Visual Basic编写它们
写一篇长文章可能需要一些时间
它们可以像任何代码一样包含错误

使用宏的好处:

您可以在键入时“录制”简单的宏
您可以构建可以与当前上下文一起工作的复杂宏
他们一按鼠标键就可以写上千个单词


例如,我可以创建一个类文件,只定义类名、基类和/或接口。声明私有成员变量后,我可以运行自定义宏,它将读取变量的名称和类型,并生成构造函数、属性以及所用基类和/或接口所需的所有方法。但是,这个特定的宏几乎有600行长。

我有一个用于创建视图模型属性的片段。这个特定的代码片段使用了其他评论者暗示的
表达式
符号

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
      <Title>View Model Property</Title>
      <Description>
          Declares a property and member suitable for Viewmodel implementation.
      </Description>
      <HelpUrl>
      </HelpUrl>
      <Shortcut>propvm</Shortcut>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <ID>propname</ID>
          <ToolTip>Property Name</ToolTip>
          <Default>Name</Default>
          <Function>
          </Function>
        </Literal>
        <Literal Editable="true">
          <ID>type</ID>
          <ToolTip>Property type.</ToolTip>
          <Default>Type</Default>
          <Function>
          </Function>
        </Literal>
        <Literal Editable="true">
          <ID>init</ID>
          <ToolTip>Member initialisation</ToolTip>
          <Default>null</Default>
          <Function>
          </Function>
        </Literal>
      </Declarations>
      <Code Language="csharp" Kind="type decl"><![CDATA[public $type$ $propname$
{
    get { return m_$propname$; }
    set 
    { 
        m_$propname$ = value;
        base.OnPropertyChanged(() => $propname$);
    }
} $type$ m_$propname$ = default($type$);$end$]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

膨胀
视图模型属性
声明适合Viewmodel实现的属性和成员。
propvm
专有名称
属性名
名称
类型
属性类型。
类型
初始化
成员初始化
无效的
$propname$);
}
}$type$m_$propname$=默认值($type$)$结束$]]>
注意对
base.PropertyChanged()的调用。
。我有一个
ViewModelBase
类来为我完成繁重的属性通知和验证工作

用法如下:

  • 键入
    propvm
  • 点击标签两次
  • 填写突出显示的字段,然后按tab键切换到下一个字段

    首先,如前所述,使用代码片段为您创建代码。 还有几个库可以帮助您使用它,即AOP

    在简单控件上的原始ui性能无关紧要的应用程序中,我已经使用了一些东西:一个带有
    字典的helper类来保存实际的属性后端,以及获取/设置任何类型属性的方法,将表达式作为参数以避免使用字符串文本。使用此选项时,属性归结为

    public int SomeProperty
    {
      get { return properties.Get( model => model.SomeProperty ); }
      set { properties.Set( model => model.SomeProperty, value ); }
    }
    
    另外,当值真正更改时,
    Set
    调用返回true,因为这通常很有用

    下面是一些代码,带有通常的“自担风险使用”等警告。您只需要一个NotifyPropertyChangedHelper实现,但这很容易找到(例如,在网上搜索'propertychanged helper',非常确定它也是发布在网上的)

    公共类NotifyPropertyChangedMap,其中T:INotifyPropertyChanged
    {
    #区域字段
    私有只读属性容器;
    私有只读字典属性;
    #端区
    #区域构造函数
    公共NotifyPropertyChangedMap(T propertyContainer)
    {
    Contract.Requires(propertyContainer!=null,“propertyContainer”);
    this.propertyContainer=propertyContainer;
    this.properties=新字典();
    }
    #端区
    #区域获取和设置
    公共属性Get(表达式)
    {
    var propName=NotifyPropertyChangedHelper.GetPropertyName(表达式);
    如果(!properties.ContainsKey(propName))
    Add(propName,GetDefault());
    返回(属性)属性[propName];
    }
    公共布尔集合(表达式、属性newValue)
    {
    var propName=NotifyPropertyChangedHelper.GetPropertyName(表达式);
    如果(!properties.ContainsKey(propName))
    {
    添加(propName,newValue);
    propertyContainer.RaisePropertyChangedEvent(propName);
    }
    其他的
    {
    if(EqualityComparer.Default.Equals((属性)属性[propName],newValue))
    返回false;
    属性[propName]=newValue;
    propertyContainer.RaisePropertyChangedEvent(propName);
    }
    返回true;
    }
    #端区
    #区域实施
    私有静态属性GetDefault()
    {
    变量类型=类型(属性);
    返回(属性)(type.IsValueType?Activator.CreateInstance(类型):null);
    }
    #端区
    }
    
    使用类似“mvvmprop”的代码片段,有很多
    public class NotifyPropertyChangedMap<T> where T : INotifyPropertyChanged
    {
      #region Fields
      private readonly T propertyContainer;
      private readonly Dictionary<string, object> properties;
      #endregion
    
      #region Constructors
      public NotifyPropertyChangedMap( T propertyContainer )
      {
        Contract.Requires<ArgumentNullException>( propertyContainer != null, "propertyContainer" );
    
        this.propertyContainer = propertyContainer;
        this.properties = new Dictionary<string, object>();
      }
      #endregion
    
      #region Get and Set
      public Property Get<Property>( Expression<Func<T, Property>> expression )
      {
        var propName = NotifyPropertyChangedHelper.GetPropertyName( expression );
        if( !properties.ContainsKey( propName ) )
          properties.Add( propName, GetDefault<Property>() );
        return (Property) properties[ propName ];
      }
    
      public bool Set<Property>( Expression<Func<T, Property>> expression, Property newValue )
      {
        var propName = NotifyPropertyChangedHelper.GetPropertyName( expression );
        if( !properties.ContainsKey( propName ) )
        {
          properties.Add( propName, newValue );
          propertyContainer.RaisePropertyChangedEvent( propName );
        }
        else
        {
          if( EqualityComparer<Property>.Default.Equals( (Property) properties[ propName ], newValue ) )
            return false;
          properties[ propName ] = newValue;
          propertyContainer.RaisePropertyChangedEvent( propName );
        }
        return true;
      }
      #endregion
    
      #region Implementation
      private static Property GetDefault<Property>()
      {
        var type = typeof( Property );
        return (Property) ( type.IsValueType ? Activator.CreateInstance( type ) : null );
      }
      #endregion
    }