I have a C# class with a number of properties that are read by and set by multiple threads. I use a simple lock when getting and setting them in order to have a full-fence memory barrier, so member data is always up to date and to avoid reordering issues. So I end up many with properties looking like this simplistic example:
public string Status
{
get
{
lock(_lock)
return _status;
}
set
{
lock(_lock)
_status = value;
}
}
In the interests of cleaner looking code, I tried to find a way to do these getters/setters as expression-bodied members. But you cannot lock and return (or set) a value in an expression bodied member. It would have to be a function call.
(I know I could condense the above get/set down to one line each -- please bear with me and ignore that for sake of this question).
So I wrote these utility functions.
protected T GetWithLock<T>(ref T value, Lock lockObj)
{
using var scope = lockObj.EnterScope();
return value;
}
protected void SetWithLock<T>(currentValue, T newValue, Lock lockObj)
{
using (lockObj.EnterScope())
currentValue = newValue;
}
Letting me turn the getters/setters into these which are much more pleasing to my eye.
public string Status
{
get => GetWithLock(ref _status, _lock);
set => SetWithLock(ref _status, value, _lock);
}
So a full class could be:
public class Widget
{
protected T GetWithLock<T>(ref T value, Lock lockObj)
{
using var scope = lockObj.EnterScope();
return value;
}
protected void SetWithLock<T>(currentValue, T newValue, Lock lockObj)
{
using (lockObj.EnterScope())
currentValue = newValue;
}
public string Status
{
get => GetWithLock(ref _status, _lock);
set => SetWithLock(ref _status, value, _lock);
}
private string _status = string.Empty;
private readonly Lock _lock = new();
}
This compiles and builds and runs just fine... I think. But is my approach still ensuring me the memory barrier for each value that I truly want? Can the compiler still know to order things around my member data correctly when I pass it by ref into a utility function like this? Or have I broken that with this approach?
I don't know how to be sure.
GetWithLockandSetWithLock. I just felt I needed to compare them with how I was doing it before to show the intent \$\endgroup\$