Элегантно обрабатывать повторяющийся код собственности в C # - PullRequest
6 голосов
/ 11 мая 2010

Языковой ярлык

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 # \ Snippets \ 1033 \ Рефакторинг \ EncapsulateField.snippet

(путь для VS 2010)

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

Ответы [ 8 ]

8 голосов
/ 11 мая 2010

Это известно как Аспектно-ориентированное программирование (AOP).

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

В зависимости от ваших потребностей существует несколько инструментов АОП.

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

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

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

3 голосов
/ 11 мая 2010

Вот то, что я смоделировал как упражнение. Первоначально вдохновленный блогом Джона Скита .

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>
2 голосов
/ 11 мая 2010

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

Идет и Гуглит

Ахаха!

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

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

1 голос
/ 17 октября 2012

Посмотрите на NotifyPropertyWeaver: https://github.com/SimonCropp/NotifyPropertyWeaver

1 голос
/ 11 мая 2010

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

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

0 голосов
/ 30 мая 2010

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

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

0 голосов
/ 11 мая 2010

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

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

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

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

0 голосов
/ 11 мая 2010

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

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

public string Code { get; setwithnotify; }

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

...