Типы ссылок по умолчанию равны null, чтобы указать, что они не ссылаются на какой-либо объект. Следовательно, если вы попытаетесь получить доступ к объекту, на который ссылаетесь, а его нет, вы получите исключение NullReferenceException.
Для Ex:
SqlConnection connection = null;
connection.Open();
Когда вы запускаете это кода, вы получите:
System.NullReferenceException: Object reference not set to an instance of an object.
Вы можете избежать этой ошибки, например, следующим образом:
if (connection != null){
connection.Open();
}
Примечание. Чтобы избежать этой ошибки, вы всегда должны инициализировать свои объекты прежде чем пытаться что-либо сделать с ними.
Вы можете посмотреть PostSharp . Они даже имеют образец в Связывании данных . Код, взятый оттуда:
/// <summary>
/// Aspect that, when apply on a class, fully implements the interface
/// <see cref="INotifyPropertyChanged"/> into that class, and overrides all properties to
/// that they raise the event <see cref="INotifyPropertyChanged.PropertyChanged"/>.
/// </summary>
[Serializable]
[IntroduceInterface( typeof(INotifyPropertyChanged),
OverrideAction = InterfaceOverrideAction.Ignore )]
[MulticastAttributeUsage( MulticastTargets.Class,
Inheritance = MulticastInheritance.Strict )]
public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect,
INotifyPropertyChanged
{
/// <summary>
/// Field bound at runtime to a delegate of the method <c>OnPropertyChanged</c>.
/// </summary>
[ImportMember( "OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;
/// <summary>
/// Method introduced in the target type (unless it is already present);
/// raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[IntroduceMember( Visibility = Visibility.Family, IsVirtual = true,
OverrideAction = MemberOverrideAction.Ignore )]
public void OnPropertyChanged( string propertyName )
{
if ( this.PropertyChanged != null )
{
this.PropertyChanged( this.Instance,
new PropertyChangedEventArgs( propertyName ) );
}
}
/// <summary>
/// Event introduced in the target type (unless it is already present);
/// raised whenever a property has changed.
/// </summary>
[IntroduceMember( OverrideAction = MemberOverrideAction.Ignore )]
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method intercepting any call to a property setter.
/// </summary>
/// <param name="args">Aspect arguments.</param>
[OnLocationSetValueAdvice,
MulticastPointcut( Targets = MulticastTargets.Property,
Attributes = MulticastAttributes.Instance)]
public void OnPropertySet( LocationInterceptionArgs args )
{
// Don't go further if the new value is equal to the old one.
// (Possibly use object.Equals here).
if ( args.Value == args.GetCurrentValue() ) return;
// Actually sets the value.
args.ProceedSetValue();
// Invoke method OnPropertyChanged (our, the base one, or the overridden one).
this.OnPropertyChangedMethod.Invoke( args.Location.Name );
}
}
Использование так же просто:
[NotifyPropertyChanged]
public class Shape
{
public double X { get; set; }
public double Y { get; set; }
}
Примеры, взятые с сайта PostSharp и вставленные для завершения ответа
Хорошо, это не очищает код, но сокращает время написания всего этого кода. Я могу теперь пропустить список из 20+ свойств за пару минут.
Сначала вам нужно определить все ваши личные переменные, я предполагаю, что ваш первый символ - это нижний регистр. Теперь скопируйте эти переменные в другой список, поскольку макрос удаляет исходную строку.
Например:
private int something1 = 0;
private int something2 = 0;
private int something3 = 0;
private int something4 = 0;
private int something5 = 0;
private int something6 = 0;
Затем поместите курсор где-нибудь на эту строку и запустите этот макрос. Опять же, это заменяет строку общедоступным свойством, поэтому убедитесь, что у вас есть одна и та же переменная частного члена, определенная до этого в вашем классе.
Я уверен, что этот скрипт можно очистить, но он спас меня утомительная работа сегодня.
Sub TemporaryMacro()
DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstText)
DTE.ActiveDocument.Selection.Delete(7)
DTE.ActiveDocument.Selection.Text = "public"
DTE.ActiveDocument.Selection.CharRight()
DTE.ExecuteCommand("Edit.Find")
DTE.Find.FindWhat = " "
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
DTE.ActiveDocument.Selection.CharRight()
DTE.ActiveDocument.Selection.WordRight(True)
DTE.ActiveDocument.Selection.CharLeft(True)
DTE.ActiveDocument.Selection.Copy()
DTE.ActiveDocument.Selection.CharLeft()
DTE.ActiveDocument.Selection.CharRight(True)
DTE.ActiveDocument.Selection.ChangeCase(VsCaseOptions.VsCaseOptionsUppercase)
DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstText)
DTE.ExecuteCommand("Edit.Find")
DTE.Find.FindWhat = " = "
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
DTE.ActiveDocument.Selection.CharLeft()
DTE.ActiveDocument.Selection.EndOfLine(True)
DTE.ActiveDocument.Selection.Delete()
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "get { return "
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = "; }"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "set"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "if("
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = " != value)"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = " = value;"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "OnPropertyChanged("""
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = """);"
DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstText)
DTE.ExecuteCommand("Edit.Find")
DTE.Find.FindWhat = """"
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = False
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.Action = vsFindAction.vsFindActionFind
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
DTE.ActiveDocument.Selection.CharRight()
DTE.ActiveDocument.Selection.CharRight(True)
DTE.ActiveDocument.Selection.ChangeCase(VsCaseOptions.VsCaseOptionsUppercase)
DTE.ActiveDocument.Selection.Collapse()
DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.LineDown()
DTE.ActiveDocument.Selection.EndOfLine()
End Sub
Джош Смит имеет хорошую статью об использовании DynamicObject, чтобы сделать это здесь
В основном это связано с наследованием от DynamicObject и последующим подключением к TrySetMember. CLR 4.0, к сожалению, хотя это может быть возможно и с использованием ContextBoundObject в более ранних версиях, но это, вероятно, повредило бы производительность, в основном для удаленных \ WCF.
IMHO, подход PostSharp, как и в принятом ответе, очень приятный и, конечно же, прямой ответ на заданный вопрос.
Однако для тех, кто не может или не будет использовать такой инструмент, как PostSharp для расширения синтаксиса языка C #, можно получить большую выгоду, чтобы избежать повторения кода с базовым классом, который реализует INotifyPropertyChanged
. Есть много примеров, лежащих вокруг, но никто до сих пор не был включен в этот полезный и хорошо подверженный торговле вопрос, так что вот версия, которую я обычно использую:
/// <summary>
/// Base class for classes that need to implement <see cref="INotifyPropertyChanged"/>
/// </summary>
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
/// <summary>
/// Raised when a property value changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Updates a field for a named property
/// </summary>
/// <typeparam name="T">The type of the field</typeparam>
/// <param name="field">The field itself, passed by-reference</param>
/// <param name="newValue">The new value for the field</param>
/// <param name="propertyName">The name of the associated property</param>
protected void UpdatePropertyField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
OnPropertyChanged(propertyName);
}
}
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">The name of the property that has been changed</param>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.DynamicInvoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Используется, например, так:
private int _value;
public int Value
{
get { return _value; }
set { UpdatePropertyField(ref _value, value); }
}
Не так лаконично, как просто применить атрибут кода к автоматически реализованному свойству, как в подходе PostSharp, но все же имеет большое значение для ускорения реализации моделей представления и других аналогичные типы.
Ключевые особенности выше, отличающие его от некоторых других реализаций:
EqualityComparer<T>.Default
. Это гарантирует, что типы значений могут сравниваться без коробки (общей альтернативой будет object.Equals(object, object)
). Экземпляр IEqualityComparer<T>
кэшируется, поэтому после первого сравнения для любого заданного типа T
он очень эффективен. OnPropertyChanged()
- virtual
. Это позволяет производным типам легко и эффективно обрабатывать события с измененными свойствами централизованно, без необходимости подписываться на событие PropertyChanged
(например, для нескольких уровней наследования), а также, конечно, дает производному типу лучший контроль над тем, как и когда он обрабатывает событие измененного свойства относительно повышения фактического события PropertyChanged
. Похоже, что Framework 4.5 немного упрощает это:
private string m_Fieldname;
public string Fieldname
{
get { return m_Fieldname; }
set
{
m_Fieldname = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
// ... do stuff here ...
}
Это не совсем автоматизирует все, что вам нужно, но используя CallerMemberNameAttribute
делает передачу имени свойства в виде ненужной строки.
Если вы работаете над Framework 4.0 с установленным KB2468871 , вы можете установить совместимость с Microsoft BCL Pack через nuget , который также предоставляет этот атрибут.