2010-05-11 7 views
6

языка контекстногоЭлегантно ручки Серийного Кода объекта в C#

public string Code 
{ 
    get; 
    set; 
} 

экономит немного набрав при определении тривиальных свойств в C#.

Однако я нахожу, что пишу очень повторяющийся, не совсем-тривиальный код свойства, который по-прежнему следует четкому шаблону, например.

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

Я могу, конечно, определить фрагмент Visual Studio, чтобы уменьшить типизацию. Однако, если мне нужно что-то добавить к моему шаблону, я должен вернуться и изменить совсем немного существующего кода.

Есть ли более элегантный подход? Является ли фрагмент лучшим способом?

UPDATE:

Как быстро улучшения для прямо сейчас, я редактировал (после создания резервной копии)

C: \ Program Files \ Microsoft Visual Studio 10.0 \ VC# \ Отрывки \ 1033 \ Refactoring \ EncapsulateField.snippet

(путь для VS 2010)

, чтобы отразить мой текущий шаблон. Теперь встроенный инструмент рефакторинга использует мой шаблон для создания свойства из поля. Недостатки: глобальные изменения для Visual Studio, не могут ретроактивно изменять существующий код свойства.

ответ

7

Это знают как Aspect Oriented Programming (AOP).

Скотт Гензель недавно сделал интервью с создателем LinFu Филиппом Лауреано по этой теме. (link)

Существует множество инструментов АОП, в зависимости от ваших потребностей.

И, наконец, некоторые реализации в INotifyPropertyChanged класса, используя указанные выше инструменты:

Update 2013: С этого оригинального ответа я пришел через другое решение, которое делает все, что нужно очень легко.

PropertyChanged.Fody (ранее NotifyPropertyWeaver) является пост-компилятором IL-ткача, который автоматически вставляет измененный вами код свойства. Это теперь мое предпочтительное решение для INotifyPropertyChanged.

+0

+1 Легко лучший и самый подходящий ответ! – Pat

-1

очень распространенный образец. Отрывок это нормально, но мальчик, не было бы замечательно, если бы MS создал некоторый синтаксический сахар для уведомлений об изменениях авто собственности ....

Что-то вроде ....

public string Code { get; setwithnotify; } 

Это действительно было бы очень хороший.

+0

Да, что бы справиться с этой конкретной модели, хотя по-прежнему оставляет общую задачу повторить некоторый произвольный шаблон - в идеале с возможностью вставки модификаций для заданного свойство - нерешено. –

+0

Это не ответ, а скорее обсуждение, поэтому его лучше разместить в качестве комментария к исходному вопросу. Кроме того, я не думаю, что это хорошая идея испечь концепции из каркаса слишком сильно в синтаксисе C#. После того, как INotifiyPropertyChange выходит из моды (просто посмотрите на свойства зависимостей), мы остаемся с унаследованной концепцией. – jpierson

2

Я не делал этого сам, но я видел, что для этой конкретной задачи используется рамка инъекции зависимостей.

Goes и Googles

Ахаха!

Кажется, используя Unity DI framework, вы можете ввести INotifyPropertyChanged в автоматическое свойство. Взгляните на этот замечательный пост в блоге: http://shecht.wordpress.com/2009/12/12/inotifypropertychanged-with-unity-interception-aop/

Это напоминает мне о недавнем HanselMinutes, где Скотт разговаривает с парнем об Аспектно-ориентированном программировании (АОП), где этот тип инъекций очень распространен.

+0

DI выглядит очень многообещающим. Хотя в вопросе не указано, я сейчас работаю с Silverlight. Удивительно, что Unity 2.0 для Silverlight также поддерживает этот шаблон (не знаю, что они должны были оставить вне стандартной версии). –

1

Ненавижу писать этот код!

В прошлом для решения этой проблемы я внедрил генератор кода для создания класса partial с определениями свойств.

+0

В прошлом я сам создал код с UML-моделей. Это отличный подход, когда проект «достаточно велик», чтобы гарантировать основанный на модели подход и создание генератора кода. Когда инструменты моделирования становятся все более мощными/интегрированными, порог «достаточно большой» продолжает снижаться. –

+0

@ Эрик: Лично я бы избегал UML и все его интеллектуальные накладные расходы. Я думаю, что ваша цель проста: классы со свойствами, которые уведомляются об изменении. Простое решение может быть скриптом PowerShell, который позволяет вам писать: 'class C {property string Code; } 'и производит частичный класс. Я делал это раньше, когда мне также нужен свободный интерфейс для этого класса. –

3

Вот что я издевался, как упражнение. Первоначально вдохновленный Jon Skeet's 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 выполняет проверку равенства и вызывает лямбду, которая фактически изменяет свойство, а также вызывает 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> 
+0

Это интересный подход к использованию методов расширения, хотя я не полностью слежу за реализацией. Какую роль играет «новый (заголовок), заголовок»? Почему не упоминается «ценность»? –

+0

Хе-хе, это на самом деле то, что Джон Скит придумал, когда, и это способ получить имя свойства во время выполнения. «Значение», на которое не ссылается, на самом деле является ошибкой, из-за того, что оно вытаскивается из другого места. Я исправлю это и расширю решение. –

+0

Спасибо за разъяснение. Конечно, интересный подход! –

-6

Чтобы ответить на общий вопрос о repetetive кода, а не только свойство случае уведомления об изменении:

Sure. Это идеальный вариант для макроса.

О, подождите. C# не имеет макросов. Разумеется, макросы являются злыми и не являются лучшим решением для любой проблемы программирования.

Если вы не введете препроцессор в свою сборку.

+1

-1: Не полезно. –

+1

Не полезно (и даже не было бы хорошего предложения, если бы вопрос был в C++) –

0

Спасибо всем за ваши ответы. Я получил полезные ответы. Однако после небольшого исследования я пришел к выводу, что лучший ответ (по крайней мере для меня и способ разработки программного обеспечения) заключается в моделировании классов в Visual Studio и использовании T4 для генерации частичных классов, реализующих свойство код.

См http://msdn.microsoft.com/en-us/library/ee329480.aspx

+0

Это в значительной степени то, что предложил Джей Базузи, не так ли? – jpierson

Смежные вопросы