繁体   English   中英

使用通用方法避免递归性设置 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 个属性,但在实际示例中却有很多。 我需要在所有属性集中应用一个逻辑,具体取决于 property4 boolean,所以我没有在我的属性的所有设置器中编写相同的代码,而是尝试创建一个通用方法来调用它们。

所以,我做了一个枚举:

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() 中递归调用属性设置器产生堆栈溢出。 所以我用一个 boolean 解决了这个话题,该 boolean 由 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 变量,因为我使用反射访问属性,因此要在逻辑中设置新值,我无法避免没有 boolean 的递归集(来自反射 ZA2F2C64F38F78BA3EA5C905AB5A2DA27Z 的属性.SetValue())设置新值再次调用集合,因此无限循环)。

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

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

我在想类似的东西

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

或其他类型的具有通用 class 属性的 setLogic 可以避免无限循环,这是一个无法想到的。

希望我让自己明白。

您是否可以只使用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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM