3

哪个是更好的编程实践,为什么?

我有这样的课:

class data {

    public double time { get; internal set; }
    public double count { get; internal set; }
    public average_count { ... }

}

其中 average_count 应该是 read_only 并给出计数/时间的计算。

将访问器编写为:

public average_count { get {

    return (time == 0) ? 0 : (count / time);

}}

或者我应该做类似的事情:

private _avg_count;

public average_count {
    get 
    {
        return _avg_count;
    }

    internal set
    {
        return _avg_count;
    }
}

在时间和计数集访问器中更新_avg_count?

似乎第一个更容易阅读,但如果经常访问 average_count 可能会更慢。编译器优化是否会使差异变得微不足道?

4

8 回答 8

13

动态执行会产生更易读的代码。预先计算可能会提高性能,但只有在 (a) 有必要并且 (b) 您已经进行了分析并且它有所作为时,您才应该这样做。

底线是,只有在绝对必要时才应该牺牲可读性和可维护性来换取性能。

于 2010-08-24T18:02:23.913 回答
5

这是一个看似简单的问题,但几乎不可能回答。原因是什么是“正确的”取决于许多因素。

1. 性能

在他的回答中, Skilldrick建议您通常更喜欢可读的内容而不是性能最佳的内容:

[R] 可读性和可维护性 只有在绝对必要时才应该为了性能而牺牲。

我会反驳说,这在典型的业务应用程序中是正确的,其中性能和功能是两个明显可区分的特征。在某些高性能软件场景中,这并不是一件容易的事,因为性能和功能可能变得密不可分——也就是说,如果你的程序完成任务的好坏取决于它执行的可靠程度(在我目前的工作地点,一家从事算法交易的公司)。

所以这是你的判断。最好的建议是在你有想法的时候进行分析;如果在您的情况下牺牲可读性以提高性能是合适的,那么您应该这样做。

2. 内存使用

0xA3提出了一种相当优雅的方法,提供了一种折衷方案:仅根据需要计算值,然后将其缓存。

当然,这种方法的缺点是它需要更多的内存来维护。an需要与plus aint?基本相同的内存量(由于对齐问题,这实际上可能意味着 64 位而不是 40 位)。如果您有加载和加载此类的实例,并且内存是您所针对的平台上的稀缺资源,那么使用每个实例另外 32 位来膨胀您的类型可能不是最明智的举措。intbooldata

3. 可维护性

也就是说,我总体上同意其他人所说的话,在其他条件相同的情况下,你最好在可读性方面犯错,这样当你将来重新访问它时,你就可以理解你的代码。然而,解决这个问题的各种方法都不是特别复杂。

最重要的是,只有您知道您的确切情况,因此您处于决定在这里做什么的最佳位置。

于 2010-08-24T18:53:16.947 回答
4

这取决于您调用的average_count频率count以及time修改的频率。对于这种优化,我建议您使用分析器。

于 2010-08-24T18:03:49.013 回答
4

在性能至关重要并且您需要经常访问该属性的情况下,您还有另一个选择:在需要时计算结果,然后缓存结果。模式如下所示:

class Foo
{
    int? _cachedResult = null;

    int _someProperty;
    public int SomeProperty
    {
        get { return _someProperty; }
        set { _someProperty = value; _cachedResult = null; }
    }

    int _someOtherProperty;
    public int SomeOtherProperty
    {
        get { return _someOtherProperty; }
        set { _someOtherProperty = value; _cachedResult = null; }
    }

    public int SomeDerivedProperty
    {
        get
        {
            if (_cachedResult == null)
                _cachedResult = someExpensiveCalculation();

            return (int)_cachedResult;
        }
    }
}
于 2010-08-24T18:23:12.263 回答
1

在这种简单的情况下,我肯定会先实现计算版本;然后根据需要进行优化。如果您存储该值,您还需要额外的代码来重新计算该值,如果它所依赖的任何值发生变化,这会导致错误的可能性。

不要过早优化。

于 2010-08-24T18:03:18.020 回答
1

我会选择你的第一个选项。这些是内存中的对象,因此对您正在做的事情的计算将非常快。此外,如果您为此创建了一个专用属性(例如,average_count),那么您将不得不添加更多代码以在设置器中重新计算时间和计数。

作为旁注(因为您要询问最佳实践),您应该在 C# 中使用 Pascal 大小写,它是初始大写字母,没有下划线。

于 2010-08-24T18:04:01.790 回答
0

编译器优化是否会使差异变得微不足道?

取决于您认为“重要”的内容。读取变量相当快。除以两个数字相当快。事实上,根据 RAM 缓存中的内容,读取变量可能需要比除法更长的时间。

使用第一种方法。如果它看起来很慢,那么考虑第二个。

于 2010-08-24T18:05:46.670 回答
0

如果您关心线程安全,则执行第二个选项可能比第一个选项更容易。

private double _avg_count;
static readonly object avgLock = new object();

public double time { get; internal set; }
public double count { get; internal set; }


public double average_count {
    get 
    {
        return _avg_count;
    }


}

private void setAverageCount()
{
    _avg_count = time == 0 ? 0 : (count / time);
}


 public void Increment()
 {
     lock (avgLock)
     {
         count += 1;
         setAverageCount();
     }


 }


 public void EndTime(double time)
 {
     lock (avgLock)
     {
         time = time;
         setAverageCount();

     }
 }
于 2010-08-24T18:41:18.267 回答