Изящно обработайте повторяющийся код свойства в C#

Ярлык языка

public string Code
{
    get;
    set;
}

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

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

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, не может задним числом изменить существующий код свойства.

6
задан Eric J. 18 February 2013 в 04:37
поделиться

7 ответов

Это известно как Aspect Oriented Программирование (AOP).

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

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

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

Обновление 2013 г .: После этого исходного ответа я нашел другое решение это делает все, что мне нужно, очень легко.

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

8
ответ дан 16 December 2019 в 21:36
поделиться

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

Идет и гуглит

Ахаха!

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

Это напомнило мне о недавнем выпуске HanselMinutes, где Скотт говорит с парнем об аспектно-ориентированном программировании (AOP), где этот тип инъекции очень часто встречается.

2
ответ дан 16 December 2019 в 21:36
поделиться

Вот то, что я придумал в качестве упражнения. Изначально вдохновила запись в блоге Jon Skeet.

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>
3
ответ дан 16 December 2019 в 21:36
поделиться

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

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

public string Code { get; setwithnotify; }

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

-1
ответ дан 16 December 2019 в 21:36
поделиться

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

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

1
ответ дан 16 December 2019 в 21:36
поделиться

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

Конечно. Это идеальный случай для макроса.

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

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

-6
ответ дан 16 December 2019 в 21:36
поделиться

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

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

0
ответ дан 16 December 2019 в 21:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: