0

I have a class with many Properties and public Setters. On each Setter I want to check if the value has changed and if so calling an EventHandler method. The code ended up looking too long, too repetitive and very programmer error prone because I can make small mistake on the multiple setter methods and mess up things.

I would like to ask if there is any way to make this code smaller and more reusable (in case I want to add new Properties for example):

using System;

public class CosmeticsModel
{
    private string _skin;
    public string Skin {
        get => _skin;
        set
        {
            if (value != _skin)
            {
                _skin = value;
                OnCosmeticChanged("Skin", _skin);
            }
        }
    }

    private string _eyes;
    public string Eyes {
        get => _eyes;
        set
        {
            if (value != _eyes)
            {
                _eyes = value;
                OnCosmeticChanged("Eyes", _eyes);
            }
        }
    }

    private string _mouth;
    public string Mouth {
        get => _mouth;
        set
        {
            if (value != _mouth)
            {
                _mouth = value;
                OnCosmeticChanged("Mouth", _mouth);
            }
        }
    }

    private string _accessory;
    public string Accessory {
        get => _accessory;
        set
        {
            if (value != _accessory)
            {
                _accessory = value;
                OnCosmeticChanged("Accessory", _accessory);
            }
        }
    }

    private string _shoes;
    public string Shoes {
        get => _shoes;
        set
        {
            if (value != _shoes)
            {
                _shoes = value;
                OnCosmeticChanged("Shoes", _shoes);
            }
        }
    }

    private string _hat;
    public string Hat {
        get => _hat;
        set
        {
            if (value != _hat)
            {
                _hat = value;
                OnCosmeticChanged("Hat", _hat);
            }
        }
    }

    private string _oneHandedWeapon;
    public string OneHandedWeapon {
        get => _oneHandedWeapon;
        set
        {
            if (value != _oneHandedWeapon)
            {
                _oneHandedWeapon = value;
                OnCosmeticChanged("OneHandedWeapon", _oneHandedWeapon);
            }
        }
    }

    // [... rest of the Class]
}
fguillen
  • 36,125
  • 23
  • 149
  • 210
  • 4
    I think [this answer](https://stackoverflow.com/a/1316417/9365244) will give you what you want. It makes use of a generic `SetField` method to set the value and raise the event. – JayV Mar 12 '21 at 11:04
  • Does this answer your question? [INotifyPropertyChanged and Auto-Properties](https://stackoverflow.com/questions/3347309/inotifypropertychanged-and-auto-properties) – SomeBody Mar 12 '21 at 11:30

1 Answers1

4

You can extract a method called SetProperty. You can also make use of CallerMemberName to automatically set the property name.

private void SetProperty(ref string property, string value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") {
    if (value != property)
    {
        property = value;
        OnCosmeticChanged(propertyName, property);
    }
}

Usage:

private string _hat;
public string Hat {
    get => _hat;
    set => SetProperty(ref _hat, value);
}
fguillen
  • 36,125
  • 23
  • 149
  • 210
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • (If some of your properties are something other than a string, you can also make this method generic fairly easily) – canton7 Mar 12 '21 at 11:11
  • Did not know about that attribute – mikelegg Mar 12 '21 at 11:16
  • I still have to add the `private string _property;` to all my Properties, haven't I? – fguillen Mar 12 '21 at 11:27
  • @fguillen yes, but it's harder to make a typo there, isn't it? Since it's just a variable name, you have the compiler checking whether you typed it wrong or not. – Sweeper Mar 12 '21 at 11:29
  • Understood, I added the missing needed code.. for C# newbies like me is better to have things crystal clear :) – fguillen Mar 12 '21 at 11:32
  • (There is a language proposal to remove the need for a separate backing field in this case, but at the moment it's still a proposal) – canton7 Mar 12 '21 at 11:33