#793 – Being Notified When an Object’s Properties Change, Part II

Once you’ve implemented the INotifyPropertyChanged interface in a class, you can subscribe to the PropertyChanged event for an instance of your class.  Assuming that you’ve implemented INotifyPropertyChanged properly, the event will fire whenever any property value in the object changes.

The code below assumes that the Dog class implements INotifyPropertyChanged.

        static void Main(string[] args)
        {
            Dog d = new Dog("Bob", 5);

            d.PropertyChanged += d_PropertyChanged;

            d.Name = "Bob Jr.";
            d.AgeThisDog();
            d.Age = 10;
        }

        static void d_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Console.WriteLine(string.Format("Property {0} just changed", e.PropertyName));
        }

793-001

The PropertyChanged event handler receives an instance of a PropertyChangedEventArgs object, which just contains a PropertyName property that tells you which property changed.  It pass any information about either the old or the new values of the property.  The assumption is that client code can read the updated property value on their own.

Advertisements

#792 – Being Notified When an Object’s Properties Change, Part I

If you write a class, there’s a pattern that you can implement, to let users of your class know when a property value changes.  This pattern is implemented by implementing the INotifyPropertyChanged in your class.

The purpose of this pattern is to let client code know that some property value has changed, in situations where that code didn’t initiate the change.  For example, this pattern is used in data binding.

Below is an example of a class that implements INotifyPropertyChanged.  The class defines an event that is fired whenever any property value changes.

    public class Dog : INotifyPropertyChanged
    {
        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                if (name != value)
                {
                    name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }

        private int age;
        public int Age
        {
            get { return age; }
            set
            {
                if (age != value)
                {
                    age = value;
                    RaisePropertyChanged("Age");
                }
            }
        }

        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public void AgeThisDog()
        {
            Age++;
        }

        //-- INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

Part II of this topic will show how client code can use this new event.

#791 – Properties Are Not Variables

A property cannot be used in the same way as a variable, like a field can.

A field is a variable that is defined within a class and it denotes an actual storage location.  Because it refers to a storage location, you can pass a field by reference, using a ref or out keyword on the argument.

If Name is a public field of a Dog class, you can do this:

        static void Main(string[] args)
        {
            Dog d = new Dog("Bob");

            MakeNoble(ref d.Name);
        }

        private static void MakeNoble(ref string name)
        {
            name = "Sir " + name;
        }

A property, on the other hand, is not a variable and its name does not refer directly to a storage location.  So you can’t pass a property as a ref or out argument to a method.

            Dog d = new Dog("Bob");
            d.Age = 5;
            AgeMe(ref d.Age);  // Compile-time Error

#790 – Property get and set Accessors Can Have Different Access Modifiers

When you implement a property in a class, you can specify different access modifiers for the get vs. set accessors.  This is true whether you are implementing the property yourself, or using an automatic property.

Different combinations of access modifiers include:

  • get/set both public – client can read/write property value
  • get/set both private – client has no access to the property
  • get public, set private – property is read-only
  • get private, set public – property is write-only
        // get/set both public
        public string Name { get; set; }

        // get/set both private
        private string SecretName { get; set; }

        // public get => read-only
        public string CalcName { get; private set; }

        // public set => write-only
        public string WriteOnlyName { private get; set; }

#788 – A Backing Field Stores Data for a Property

When you create a property, you’re creating a set of accessor methods to read and write the property’s value.  You also typically need a place to store the actual property value.

The data member where the property’s value is actually stored is known as a backing field, typically defined as a private field.

When you create an auto-implemented property, you don’t explicitly declare a backing field, but the compiler creates one for you in the IL that is generated.  Your code doesn’t have access to the field.

        // Backing field not declared, created automatically
        public int Age { get; set; }

If you create the property explicitly, then you’ll declare the backing field yourself.

        // Backing field
        private int age;

        // Property
        public int Age
        {
            get { return age; }
            set
            {
                if (value != age)
                    age = value;
            }
        }

#702 – An Automatic Property Must Define Both get and set Accessors

When you define an automatic property, you must include both get and set accessors.

        public string Name { get; set; }

It wouldn’t make sense to omit either accessor, since they are the only mechanism for reading from or writing to the property.

Although you can’t strictly create a read-only or write-only automatic property, you can use access modifiers so that the property is effectively read-only or write-only, from outside the class.

        // Automatic property that is read-only from outside class
        public string Temperament { get; protected set; }

        // Automatic property that is write-only from outside class
        public string Password { protected get; set; }

#701 – Centralize Business Rules Logic in set Accessors

You can use a property’s set accessor to enforce business rules for that property, e.g. min/max values for the property.

If you have a constructor that accepts initial values for some properties of a class, or methods that accept values, you can avoid checking constraints on these parameters by delegating the enforcement of the rules to the set accessor.

For example:

    public class Dog
    {
        // Public properties
        public string Name { get; set; }

        private int age;
        public int Age
        {
            get { return age; }

            // Set accessor checks for valid Age values
            set
            {
                if (value != age)
                {
                    if ((value > 0) && (value < 30))
                        age = value;
                    else
                        throw new Exception("Age is out of range");
                }
            }
        }

        // Constructor
        public Dog(string name, int age)
        {
            Name = name;

            // Don't validate age value here, but let set accessor do it
            Age = age;
        }
    }