使用设置类属性c#的泛型方法避免递归性



我在一个更大的类中用一个更小的易于遵循的确切例子来解释我的问题。我有一个相当大的类,有很多不同类型的属性,获取和设置它们各自的类变量。

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;}
}

我在示例中列出了4个属性,但在实际示例中有很多。我需要在所有属性的集合中应用一个逻辑,这取决于property4 boolean,所以我没有在属性的所有setter中编写相同的代码,而是尝试在所有属性中调用一个泛型方法。

所以,我做了一个枚举:

public enum properties {
property1,
property2,
property3,
property4
}

这样我就可以用一个涉及反射的方法来设置我的属性,将属性类型作为参数:

public void setLogic<T>(properties property, T value) {
//irrelevant code
}

所以我的二传手变成了:

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) };}
}

当在我的setLogic((中,属性setter被递归调用,产生堆栈溢出时,我的问题就来了。因此,我用一个由setLogic((控制的布尔值来解决这个主题,该布尔值控制从哪里调用setter。所以现在我的属性变成:

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) };}
}

代码运行良好,但为了避免递归性,setter bool控件丢弃了SetLogic((泛型方法带来的所有可能的cleanes。另一方面,我不能在setLogic方法中设置类变量,因为我使用反射来访问属性,所以为了在逻辑中设置新值,我不能避免没有布尔值的递归集(反射类的property.SetValue((设置了再次调用该集的新值,所以无限循环(。

如果我不这样做,我必须粘贴setLogic((方法,而不是通用的,为集合中的每个属性复制粘贴,这也不是很干净的代码。

没有一个干净的解决方案,其中setter可以作为参数传递,或者一个避免无限递归集的通用方法吗?

我在想类似的东西

private setLogic<T>(Action<> setterMethod, T value) {
//so that the property involved might be already in the setter?
}

或其他类型的具有通用类属性的setLogic,以避免a无法想到的无限循环。

希望我能让别人理解我自己。

是否可以直接使用ref参数设置字段?:

int property1
{
get => _property1;
set => setLogic(ref _property1, value);
}
private void setLogic<T>(ref T field, T value)
{
field = value;
}

在实现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));
}
}

您可以使用[CallerMemberName]属性和Dictionary<string, object>来存储属性。这不会产生反射所带来的性能损失。

例如。。。

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

和基类。。。

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);
}

最新更新