有没有办法定义私有属性的 getter/setter,而无需第二个属性来保存该值

本文关键字:属性 第二个 保存 定义 有没有 getter setter | 更新日期: 2023-09-27 18:36:22

我有一个公共只读属性,它根据其他几个私有变量计算其值。我从计算的属性中读了很多。计算中使用的变量很少更改。我目前让只读的 getter 执行计算逻辑,但是当依赖变量更改时,将新值推送到属性中对我来说更有意义。

我当前代码的示例:

public class rectangle()
{
    public rectangle(int height, int width)
    {
        _height = height;
        _width = width;
    }
    public void resize(int heightOffset, int widthOffset)
    {
        _height += heightOffset;
        _width += widthOffset;
    }
    private int _height;
    private int _width;
    public int area
    {
        get
        {
            return _height * _width;
        }
    }
}

上面的代码过于简化,但很好地展示了我的意思。 _height和_width可以改变,但他们很少这样做。该地区属性是从很多...我估计 _height/_width 每读取 100,000+ 次 area 属性就会更改一次。

现在,如果只是乘法,我不会担心...但是实际的计算很复杂,我看到很多运行时间被沉浸在重复的多余计算中。

我知道我可以执行以下操作来修复它:

...
private int _height;
private int height
{
    set
    {
        _height = value;
        _area = _height * _width;
    }
}
private int _width;
private int width
{
    set
    {
        _width = value;
        _area = _height * _width;
    }
}
private int _area;
public int area
{
    get
    {
        return _area;
    }
}
...

鉴于我的情况,上面的代码更有意义,但在我看来,必须有更好的方法来做到这一点。我知道,鉴于我的第一个示例,我可以摆脱属性并将计算逻辑移动到 resize() 方法......但在实际代码中,影响私有值的不仅仅是一种方法。我不想梳理我的代码来查找每个事件,而且这样做似乎不可维护。

第二种方法是要走的路吗?我知道它有效,但感觉有些不对劲。我认为我担心的是,将私有属性将其值存储到私有变量中似乎很奇怪。我可能想多了。

有没有办法定义私有属性的 getter/setter,而无需第二个属性来保存该值

你知道属性访问器可以有访问修饰符吗?

例如:

public string A
{
    internal get
    {
        // Whatever
    }
    private set
    {
        // Whatever
    }
}

这也适用于自动属性:

public string A
{
    internal get;
    private set;
}

更新:好的,现在您知道了访问器访问修饰符,那这个呢?

public class rectangle()
{
    public rectangle(int height, int width)
    {
        Height = height;
        Width = width;
    }
    public int Height { get; private set; }
    public int Width { get; private set; }
    public int Area { get; private set; }
    public void resize(int heightOffset, int widthOffset)
    {
        Height += heightOffset;
        Width += widthOffset;
        RefreshArea();
    }
    private void RefreshArea() 
    {
        Area = Height * Width;
    }
}

再见了,私家田!

只要您确保始终使用高度/宽度属性来设置_height和_width,您建议的方法就可以了。

对私有变量使用私有属性并没有错。 它将更新代码移动到单个位置,以便更易于维护。

正如 Matías 所说,您可以使高度和宽度属性具有具有不同访问修饰符的访问器,以便您可以读取,但不能写入此类之外的高度/宽度属性。

您说您从代码中的许多位置修改私有字段,而不是总是遍历这些字段的属性。 最好的情况是,您将访问权限限制为仅调用属性,然后确定您只有几个地方调用重新计算。

但是,如果您不能或不会这样做,那么您是对的,确保每次修改其中一个私有字段时都必须确保计算相应的字段会很痛苦。

您还说性能是一个问题,您希望节省调用计算例程的时间。

因此,如果您必须保留对私有字段的访问权限,也许您可以将重新计算作为这些私有字段实际更改的条件。喜欢这个:

private int _height; 
private int height 
{ 
    set 
    { 
        _height = value; 
    } 
} 
private int _width; 
private int width 
{ 
    set 
    { 
        _width = value; 
    } 
} 
private int _area; 
// have some variables to store the values used in the calculation
private int _areaWidth;
private int _areaHeight;
public int area 
{ 
    get 
    { 
        // only do the calculation if these values have changed
        if(_areaWidth != _width || _areaHeight != _height)
        {
            _areaWidth = _width;
            _areaHeight = _height;
            _area = _width * _height;
        }
        return _area; 
    } 
} 

这样,您可以将计算保存在一个位置,并且仅在值更改时才重新计算。 进行条件检查比实际计算的成本要低。

我通常这样做的一种方法是这样的:

class Rectangle
{
    int _height;
    public int height { get { return _height; } set { _height = value; _area = null; }}
    int _width;
    public int width { get { return _width; } set { _width = value; _area = null; } }
    int? _area;
    public int area
    {
        get
        {
            if(!_area.HasValue)
                _area = _width * _height;
            return _area.Value;
        }
    }
}

注意:为了便于阅读,我将使用传统的 C# 编码风格(即:类名和公共属性的大写字母)。我建议你也这样做

解决问题的一种方法是简单地使Rectangle类不可变,并对该区域使用Lazy<T>来延迟计算昂贵的东西:

public class Rectangle
{
    private readonly Lazy<int> _area;
    public Rectangle(int height, int width)
    {
        Height = height;
        Width = width;
        _area = new Lazy<int>(() => Height * Width);
    }
    public int Height { get; private set; }
    public int Width { get; private set; }
    public int Area
    {
        get
        {
            return _area.Value;
        }
    }
}

不可变类/结构的好处是数不胜数的。 缺点是,每当你想要"更改"一个Rectangle对象时,你必须使用newRectangle的静态成员,例如:

...
public static Rectangle Widen(Rectangle rect, int widenByAmount)
{
    return new Rectangle(rect.Height, rect.Width + widenByAmount);
}
...

如果您有 LinqPad,请尝试以下操作:

void Main()
{
    Rectangle r = new Rectangle(5, 10);
    r.Height.Dump();
    r.Width.Dump();
    r.Area.Dump();
    r.Area.Dump();
    r = Rectangle.Widen(r, 10);
    r.Area.Dump();
    r.Area.Dump();
}
// Define other methods and classes here
public class Rectangle
{
    private readonly Lazy<int> _area;
    public Rectangle(int height, int width)
    {
        Height = height;
        Width = width;
        _area = new Lazy<int>(() =>
            {
                "Calculating...".Dump();
                return Height * Width;
            });
    }
    public int Height { get; private set; }
    public int Width { get; private set; }
    public int Area
    {
        get
        {
            return _area.Value;
        }
    }
    public static Rectangle Widen(Rectangle rect, int widenByAmount)
    {
        return new Rectangle(rect.Height, rect.Width + widenByAmount);
    }
}

对我来说,它吐出:

5
10
Calculating...
50
50
Calculating...
100
100