I follow to explain my problem in a much bigger class with an smaller easy to follow exact example. I got quite a big class, with a lot of properties of different types, getting and setting their respective class variable.
public class Foo() {
int property1 { get => _property1 ; set => _property1 = value;}
string property2 { get => _property2 ; set => _property2 = value;}
Vector3 property3 { get => _property3 ; set => _property3 = value;}
bool property4 { get => _property3 ; set => _property4 = value;}
}
I put 4 properties in the example, but in the real example there are a lot. I need to apply a logic in the set of all the properties, depending on property4 boolean, so instead of writting the same code in all the setters of my properties, I tried to make a generic method to be called in all of them.
So, I made a an enum:
public enum properties {
property1,
property2,
property3,
property4
}
So that I can set my properties with a method that involves reflection, taking the property type as an argument:
public void setLogic<T>(properties property, T value) {
//irrelevant code
}
So my setters turn to be:
public class Foo() {
int property1 { get => _property1 ; set { setLogic(properties.property1 , value) };}
string property2 { get => _property2 ; set { setLogic(properties.property2 , value) };}
Vector3 property3 { get => _property3 ; set { setLogic(properties.property3 , value) };}
bool property4 { get => _property4 ; set{ _property4 = value) };}
}
My problem comes when in my setLogic() the property setter is called recursively producing a stack overflow. So I solved the topic with a boolean controlled from setLogic() that controlls where the setter is being called from. So now my properties turn to be:
public class Foo() {
int property1 {
get => _property1;
set {
if (!_calledFromSetLogic)
setLogic(properties.property1 , value);
else {
_property1 = value;
_calledFromSetLogic = false;
}
}
}
string property2 {
get => _property2;
set {
if (!_calledFromSetLogic)
setLogic(properties.property2 , value);
else {
_property2 = value;
_calledFromSetLogic = false;
}
}
}
Vector3 property3 {
get => _property3;
set {
if (!_calledFromSetLogic)
setLogic(properties.property3 , value);
else {
_property3 = value;
_calledFromSetLogic = false;
}
}
}
bool property4 { get => property4; set{ _property4 = value) };}
}
The code works fine, but the setter bool control to avoid recursivity throws away all the possible cleannes brought by the SetLogic() generic method. On the other hand I cannot set the class variables in the setLogic method because I access the properties with reflection, so to set the new value in the logic I cannot avoid the recursive set without the boolean (property.SetValue() from the reflection class sets the new value calling the set once again, so infinite loop).
If I dont do this, I have to paste the setLogic() method, instead of been generic, copy pasted for each of the properties in the set, which is not very clean code neither.
Is there not a clean solution for this, where the setter can be passed as an argument, or a generic method that avoids infinite recursive set?
I was thinking of something like
private setLogic<T>(Action<> setterMethod, T value) {
//so that the property involved might be already in the setter?
}
or other kind of setLogic with the generic class properties that avoids the infinite loop, of whom a cannot think of.
Hope I made myself understood.
Are you able to just use a ref
parameter to set the field directly?:
int property1
{
get => _property1;
set => setLogic(ref _property1, value);
}
private void setLogic<T>(ref T field, T value)
{
field = value;
}
I commonly use this pattern when implementing INotifyPropertyChanged
:
private int _someProperty;
public int SomeProperty
{
get => _someProperty;
set => SetProperty(ref _someProperty, value);
}
private void SetProperty<T>(ref T field, T value, [CallerMemberName] propertyName = "")
{
if (!field.Equals(value))
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You could make use of the [CallerMemberName]
attribute and a Dictionary<string, object>
to store the properties in. This won't incur the same performance penalties reflection would.
For example...
class Foo : PropertyChangeNotifier
{
public int property1 { get { return Get<int>(); } set { Set(value); } }
public string property2 { get { return Get<string>(); } set { Set(value); } }
public Vector3 property3 { get { return Get<Vector3>(); } set { Set(value); } }
public bool property4 { get { return Get<bool>(); } set { Set(value); } }
protected override void OnSet<T>(string property, T value)
{
// do something meaningful.
}
}
...and the base class...
abstract class PropertyChangeNotifier
{
private readonly Dictionary<string, object> properties = new Dictionary<string, object>();
protected T Get<T>([CallerMemberName] string property = null)
{
return (T)properties[property];
}
protected void Set<T>(T value, [CallerMemberName] string property = null)
{
OnSet(property, value);
properties[property] = value;
}
protected abstract void OnSet<T>(string property, T value);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.