3

我有一个公共只读属性,它根据其他几个私有变量计算它的值。我从计算的属性中读了很多。计算中使用的变量很少改变。我目前让只读的 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 可以改变,但他们很少这样做。area 属性被大量读取...我估计每读取 area 属性 100,000+ 次,_height/_width 就会更改一次。

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

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

...

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() 方法......但在实际代码中,它不仅仅是影响私有值的一种方法。我不想梳理我的代码来查找每一个事件,而且这样做似乎不可维护。

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

4

5 回答 5

4

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

例如:

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

再见,私人领域!

于 2012-06-06T16:17:44.487 回答
0

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

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

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

于 2012-06-06T16:23:23.867 回答
0

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

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

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

因此,如果您必须保留对私有字段的访问权限,也许您可​​以根据这些私有字段实际更改的时间来重新计算。像这样:

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

这样,您可以将计算保存在一个地方,并且只有在值发生变化时才重新计算。进行条件检查比您的实际计算成本要低。

于 2012-06-06T16:29:01.897 回答
0

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

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;
        }
    }
}
于 2012-06-06T16:29:25.363 回答
0

注意:为了便于阅读,我将使用传统的 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您必须使用的对象new或类似的静态成员时Rectangle

...
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
于 2012-06-06T16:34:40.010 回答