Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用泛型方法设置类属性c避免递归性#_C#_Properties_Setter - Fatal编程技术网

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

C# 使用泛型方法设置类属性c避免递归性#,c#,properties,setter,C#,Properties,Setter,我会在一个大得多的班级里,用一个小得多、简单易懂的确切例子来解释我的问题。 我得到了一个相当大的类,有很多不同类型的属性,获取和设置它们各自的类变量 public class Foo() { int property1 { get => _property1 ; set => _property1 = value;} string property2 { get => _property2 ; set => _property2 = value;}

我会在一个大得多的班级里,用一个小得多、简单易懂的确切例子来解释我的问题。 我得到了一个相当大的类,有很多不同类型的属性,获取和设置它们各自的类变量

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
}
当在我的setLogic()中递归调用属性setter产生堆栈溢出时,就会出现问题。因此,我用一个由setLogic()控制的布尔值解决了这个问题,它控制从何处调用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) };}
}
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()泛型方法带来的所有可能的cleannes。另一方面,我无法在setLogic方法中设置类变量,因为我使用反射访问属性,所以要在逻辑中设置新值,我无法避免没有布尔值的递归集(反射类中的property.SetValue()再次设置调用该集的新值,因此无限循环)

如果我不这样做,我必须粘贴setLogic()方法,而不是泛型的,为集合中的每个属性复制粘贴,这也不是非常干净的代码

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

我在想类似的事情

private setLogic<T>(Action<> setterMethod, T value) {
    //so that the property involved might be already in the setter?
}
private setLogic(Action setterMethod,T值){
//这样所涉及的属性可能已经在setter中了?
}
或者其他类型的集合逻辑,具有避免无限循环的泛型类属性,而a无法想到无限循环


希望我能理解。

您能直接使用
ref
参数设置字段吗?:

int属性1
{
get=>\u属性1;
set=>setLogic(参考属性1,值);
}
私有无效设置逻辑(参考T字段,T值)
{
字段=值;
}
我在实现INotifyPropertyChanged时通常使用此模式:

private int\u someProperty;
公共属性
{
get=>\u someProperty;
set=>SetProperty(ref\u someProperty,value);
}
私有void SetProperty(参考T字段,T值,[CallerMemberName]propertyName=”“)
{
如果(!field.Equals(value))
{
字段=值;
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
}

您可以使用
[CallerMemberName]
属性和
字典来存储属性。这不会产生与反射相同的性能损失

例如

class Foo:PropertyChangeNotifier
{
公共int属性1{get{return get();}set{set(value);}}
公共字符串属性2{get{return get();}set{set(value);}}
公共向量3属性3{get{return get();}set{set(value);}}
公共bool属性4{get{return get();}set{set(value);}}
受保护的覆盖无效开始(字符串属性,T值)
{
//做一些有意义的事情。
}
}
…还有基类

抽象类PropertyChangeNotifier
{
私有只读字典属性=新字典();
受保护的T Get([CallerMemberName]字符串属性=null)
{
返回(T)属性[属性];
}
受保护的无效集(T值,[CallerMemberName]字符串属性=null)
{
起始(属性、值);
属性[属性]=值;
}
受保护的抽象无效开始(字符串属性,T值);
}

与直接设置属性值相比,反射是一种非常缓慢的设置属性值的方法,而且您还没有明确说明以这种方式设置属性的目的;照目前的情况,第一个代码段肯定是最干净的,您通过在所有setter中调用setLogic来“复制”代码,只是以不同的方式复制它。在任何情况下,您都应该包含setLogic函数,因为就目前情况而言,这个问题缺乏明确性,也没有一个关键组件来确定它为什么会导致递归。我试图弄清楚是什么导致了这里的递归:(反射类中的property.SetValue()设置了新的值,再次调用集合,所以是无限循环)。如果对此有任何疑问,或者我可以给出任何进一步的解释,我将非常乐意。我的建议是使用私有字段来支持属性,并在setLogic函数中设置该字段,而不是调用属性setter。或者,在调用setter之前检查新值是否与现有值相同。或者,我建议这样做,主要是因为以这种方式编写代码的唯一原因是干净,请返回使用第一个代码段-它是标准化方面所有可用选项中最干净的选项。+1用于检查setter中的新值是否相同。关于“返回到使用第一个代码段”,我解释了为什么setter中所需的逻辑无法访问私有变量,因为我通过反射访问属性,导致无法使用第一个简单的代码段,这将是理想的选择我很抱歉,我现在重读了这个问题,明白了为什么这不是一个有用的建议——工作后的大脑让我误解了。我很高兴Jeff E能够更好地理解问题背后的原因。fck我正是在写这篇文章。我认为这是个好办法。作为MVVM灯光设定器。这个答案是索尔