Вот то, что я смоделировал как упражнение. Первоначально вдохновленный блогом Джона Скита .
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>