Search code examples
c#class-variablesshorthand

Is there a shorthand for properties where I implement a custom method?


public class ServerState
{
    public static Action stateChanged;
    private string currentMap;

    public string CurrentMap
    {
        get { return currentMap; }
        set
        {
            currentMap = value;
            stateChanged?.Invoke();
        }
    }
}

I have a class with dozens of variables that each need their own property so that I can invoke an action. In other words, I am going to repeat the above code dozens of times, and I feel that there should be a better way to do so.

Is there a shorthand for the above code?


Solution

  • Here are a couple of ideas:

    using System;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    
    namespace ConsoleApp1
    {
        public class ServerState
        {
            public static Action stateChanged;
    
            private Dictionary<string, object> _values = new Dictionary<string, object>();
            private void Set(object value, [CallerMemberName] string propertyName = null)
            {
                _values[propertyName] = value;
                stateChanged?.Invoke();
            }
    
            private T Get<T>([CallerMemberName] string propertyName = null)
            {
                if (_values.TryGetValue(propertyName, out var v)) return (T)v;
                throw new KeyNotFoundException(propertyName);
            }
    
            public string CurrentMap 
            { 
                get => Get<string>(); 
                set => Set(value); 
            }
    
            // You can make them in one line if you want
            public string CurrentMap2 { get => Get<string>(); set => Set(value); }
        }
    
        public class ServerState2
        {
            public static Action stateChanged;
            private string currentMap;
    
            private void Set<T>(ref T property, T value)
            {
                property = value;
                stateChanged?.Invoke();
            }
    
            public string CurrentMap
            {
                get => currentMap;
                set => Set(ref currentMap, value);
            }
        }
    }
    
    

    And if you want to use more than one event, or track if the value actually changed you can expand on this idea:

    public class ServerState
    {
        public static Action currentMapChanged;
        public static Action currentMap2Changed;
        private Dictionary<string, object> _values = new Dictionary<string, object>();
        private void Set(object value, Action onChange, [CallerMemberName] string propertyName = null)
        {
            var previousValue = _values[propertyName];
            // Check if value has changed
            if (value != previousValue)
            {
                _values[propertyName] = value;
                onChange?.Invoke();
            }
        }
    
        private T Get<T>([CallerMemberName] string propertyName = null)
        {
            if (_values.TryGetValue(propertyName, out var v)) return (T)v;
            throw new KeyNotFoundException(propertyName);
        }
    
        public string CurrentMap 
        { 
            get => Get<string>(); 
            // Call `currentMapChanged` if value differs
            set => Set(value, currentMapChanged); 
        }
    
        // You can make them in one line if you want
        // Call `currentMap2Changed` if value differs
        public string CurrentMap2 { get => Get<string>(); set => Set(value, currentMap2Changed); }
    }
    

    Additionally, you should look into using the event keyword to stop other classes from firing this event. Watch out for static events too, they hold a reference to the listening delegate in a static context and require care to avoid memory leaks.