INotifyPropertyChanged bug introduced by refactoring, misspelling, case-mismatch etc.
Sometimes we change the name of some of our properties, and we forget to change the "string" we pass toOnPropertyChanged()
method (especially when we call this method from somewhere else), or simply misspell it, including case-mismatch.
Something like this:
public string FirstName //earlier it was simply 'Name'
{
get { return m_name; }
set
{
m_name = value ;
OnPropertyChanged("Name"); //still 'Name'. Bug!
//or, OnPropertyChanged("FirstNane"); //changed, but misspelled. Bug!
}
}
Fix
protected void OnPropertyChanged(string propertyName)
{
//this raises exception if there is something wrong (only in debug mode!).
RuntimeAssert.ValidatePropertyName(this, propertyName);
//your code here
}
Here is the implementation of RuntimeAssert class.
public static class RuntimeAssert
{
private static Dictionary<Type, List<string>> ClassPropertyMap = new Dictionary<Type, List<string>>();
private static List<string> GetProperties(Type type)
{
if (!ClassPropertyMap.ContainsKey(type))
{
PropertyInfo[] props = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
ClassPropertyMap.Add(type, new List<string>(props.Where(p => true).Select(p => p.Name)));
}
return ClassPropertyMap[type];
}
[Conditional("DEBUG")]
public static void ValidatePropertyName(object instance, string propertyName)
{
ValidatePropertyName(instance.GetType(), propertyName);
}
[Conditional("DEBUG")]
public static void ValidatePropertyName(Type type, String propertyName)
{
List<string> properties = RuntimeAssert.GetProperties(type);
if (!properties.Contains(propertyName))
{
string message = String.Format("Property '{0}' not found in class '{1}'", propertyName, type.FullName);
throw new PropertyNotFoundException(message);
}
}
}
And finally, PropertyNotFoundException (it's used in above class).
public class PropertyNotFoundException : Exception
{
public PropertyNotFoundException(string message) : base(message)
{
}
}