避免使用通用方法递归来设置 class 属性 c#
Avoid recursivity with generic method to set class properties c#
我按照一个更大的 class 来解释我的问题,并使用一个更小、更容易理解的确切示例。
我有相当大的 class,有很多不同类型的属性,获取和设置它们各自的 class 变量。
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个属性,但在真实示例中有很多。
我需要在所有属性集中应用一个逻辑,具体取决于 属性4 布尔值,因此我没有在属性的所有 setter 中编写相同的代码,而是尝试制作一个通用的在所有这些方法中调用的方法。
所以,我做了一个枚举:
public enum properties {
property1,
property2,
property3,
property4
}
这样我就可以使用涉及反射的方法设置我的属性,将 属性 类型作为参数:
public void setLogic<T>(properties property, T value) {
//irrelevant code
}
所以我的 setter 变成了:
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() 泛型方法带来的所有可能的清洁。另一方面,我无法在 setLogic 方法中设置 class 变量,因为我通过反射访问属性,因此要在逻辑中设置新值,我无法避免没有布尔值的递归集(property.SetValue () 从反射 class 设置新值再次调用集合,如此无限循环)。
如果我不这样做,我必须粘贴 setLogic() 方法,而不是通用的,为集合中的每个属性复制粘贴,这也不是很干净的代码。
是否没有一个干净的解决方案,其中 setter 可以作为参数传递,或者没有避免无限递归集的通用方法?
我在想
private setLogic<T>(Action<> setterMethod, T value) {
//so that the property involved might be already in the setter?
}
或其他类型的 setLogic 具有避免无限循环的通用 class 属性,其中 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.
}
}
...和基础 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);
}
我按照一个更大的 class 来解释我的问题,并使用一个更小、更容易理解的确切示例。 我有相当大的 class,有很多不同类型的属性,获取和设置它们各自的 class 变量。
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个属性,但在真实示例中有很多。 我需要在所有属性集中应用一个逻辑,具体取决于 属性4 布尔值,因此我没有在属性的所有 setter 中编写相同的代码,而是尝试制作一个通用的在所有这些方法中调用的方法。
所以,我做了一个枚举:
public enum properties {
property1,
property2,
property3,
property4
}
这样我就可以使用涉及反射的方法设置我的属性,将 属性 类型作为参数:
public void setLogic<T>(properties property, T value) {
//irrelevant code
}
所以我的 setter 变成了:
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() 泛型方法带来的所有可能的清洁。另一方面,我无法在 setLogic 方法中设置 class 变量,因为我通过反射访问属性,因此要在逻辑中设置新值,我无法避免没有布尔值的递归集(property.SetValue () 从反射 class 设置新值再次调用集合,如此无限循环)。
如果我不这样做,我必须粘贴 setLogic() 方法,而不是通用的,为集合中的每个属性复制粘贴,这也不是很干净的代码。
是否没有一个干净的解决方案,其中 setter 可以作为参数传递,或者没有避免无限递归集的通用方法?
我在想
private setLogic<T>(Action<> setterMethod, T value) {
//so that the property involved might be already in the setter?
}
或其他类型的 setLogic 具有避免无限循环的通用 class 属性,其中 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.
}
}
...和基础 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);
}