优雅地处理C#中的重复属性代码

时间:2010-05-11 04:43:07

标签: c# visual-studio c#-4.0 properties

语言快捷方式

public string Code
{
    get;
    set;
}

在C#中定义普通属性时节省了一些输入。

然而,我发现自己编写的是高度重复的,不是非常简单的属性代码,仍然遵循明确的模式,例如:

public string Code
{
    get { return code; }
    set 
    {
        if (code != value)
        {
            code = value; 
            NotifyPropertyChanged("Code");
        }
    }
}

我当然可以定义一个Visual Studio代码段来减少输入。但是,如果我需要在我的模式中添加一些内容,我必须返回并更改相当多的现有代码。

有更优雅的方法吗?片段是最好的方式吗?

更新:

作为目前的快速改进,我已编辑(在备份后)

C:\ Program Files \ Microsoft Visual Studio 10.0 \ VC#\ Snippets \ 1033 \ Refactoring \ EncapsulateField.snippet

(路径适用于VS 2010)

反映我目前的模式。现在,内置的重构工具使用我的模板从字段创建属性。缺点:Visual Studio的全局更改无法追溯更改现有属性代码。

8 个答案:

答案 0 :(得分:8)

这被称为Aspect Oriented Programming(AOP)。

斯科特汉塞尔曼最近接受了LinFu的创始人菲利普劳罗亚诺的采访。 (link

根据您的需要,有许多AOP工具。

最后,使用上述工具对INotifyPropertyChanged类进行了一些实现:

2013年更新:由于这个原始答案,我遇到了另一种解决方案,可以轻松完成我需要的所有工作。

PropertyChanged.Fody(以前称为NotifyPropertyWeaver)是一个后编译IL编织器,它会自动为您插入属性更改代码。现在,这是我对INotifyPropertyChanged的首选解决方案。

答案 1 :(得分:3)

这是我嘲笑的一种练习。最初的灵感来自Jon Skeet的blog post

public static class ObjectExtensions {
    public static string GetPropertyNameAndValue<T>(this T obj, out object value) {
        System.Reflection.PropertyInfo[] objGetTypeGetProperties = obj.GetType().GetProperties();
        if(objGetTypeGetProperties.Length == 1) {
            value = objGetTypeGetProperties[0].GetValue(obj, null);
            return objGetTypeGetProperties[0].Name;
        } else
            throw new ArgumentException("object must contain one property");
    }
}

class Whatever {
 protected void ChangeProperty<T>(object property, T newValue, Action change) {
     object value;
     var name = property.GetPropertyNameAndValue(out value);

     if(value == null && newValue != null || value != null && !value.Equals(newValue)) {
         change();
         OnPropertyChanged(name);
     }
 }

 private string m_Title;
 public string Title {
     get { return m_Title; }
     set {ChangeProperty(
               new { Title }, //This is used to dynamically retrieve the property name and value
               value, // new value
               () => m_Title = value); //lambda to change the value 
     }
 }
}

这是我能想到的最好的。运行时性能命中率可能相当高,但我还没有测试过。

对上述解决方案的一些解释。 new { Title }创建一个匿名对象,由于项目(在.NET 3.5中引入),新创建的对象具有一个名为Title的属性,其值为Title属性的值。原始对象。

GetPropertyNameAndValue是执行所有有趣工作的函数 - 它从匿名对象中检索属性的名称和值。 ChangeProperty然后执行相等性检查并调用实际更改属性的lambda并调用NotifyPropertyChanged

或者你可以像这样做一个片段:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propfullinotify</Title>
            <Shortcut>propfullinotify</Shortcut>
            <Description>Code snippet for property and backing field with INotifyPropertyChanged</Description>
            <Author>Microsoft Corporation</Author>
            <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>
                <Literal>
                    <ID>field</ID>
                    <ToolTip>The variable backing this property</ToolTip>
                    <Default>myVar</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
      <![CDATA[private $type$ $field$;

    public $type$ $property$
    {
        get { return $field$;}
        set {
      if ($field$ != value)
      {
        $field$ = value;
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("$property$"));
      }
    }
    }
    $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

答案 2 :(得分:2)

我自己没有这样做,但我已经看到了一个依赖注入框架用于这个特定的任务。

Goes and Googles

Ahaha!

看来,使用Unity DI framework,您可以将INotifyPropertyChanged注入自动属性。看看这篇精彩的博文:http://shecht.wordpress.com/2009/12/12/inotifypropertychanged-with-unity-interception-aop/

这让我想起了最近的HanselMinutes,斯科特正在与一个关于面向方面编程(AOP)的人谈论这种类型的注射非常常见。

答案 3 :(得分:1)

我讨厌写那段代码!

过去,为了解决这个问题,我已经实现了一个代码生成器来生成一个带有属性定义的partial类。

答案 4 :(得分:1)

答案 5 :(得分:0)

感谢大家的回复。我赞成了有用的答案。然而,经过相当多的研究,我开始相信最好的答案(至少对我和我开发软件的方式)是在Visual Studio中对类进行建模,并使用T4代码生成实现属性的部分类代码。

请参阅http://msdn.microsoft.com/en-us/library/ee329480.aspx

答案 6 :(得分:-1)

一种非常常见的模式。片段是好的,但男孩,如果MS为自动属性更改通知创建了一些语法糖,那不是很好....

像......那样的东西。

public string Code { get; setwithnotify; }

那确实非常好。

答案 7 :(得分:-6)

回答有关重复代码的一般问题,而不仅仅是财产变更通知案例:

不确定。这是宏的一个完美案例。

等等哦。 C#没有宏。当然,宏是邪恶的,并不是任何编程问题的最佳解决方案。

除非您在构建中插入预处理器。