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?
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.