C# 访问器应该使用私有变量还是动态计算?

发布于 2024-09-16 00:03:30 字数 676 浏览 11 评论 0原文

哪种编程实践更好?为什么?

我有一个这样的类:

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可能会更慢。编译器优化会使差异变得微不足道吗?

Which is a better programming practice and why?

I have a class like this:

class data {

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

}

Where average_count should be read_only and give a calculation of count / time.

Is it better to write the accessor as:

public average_count { get {

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

}}

Or should I do something like:

private _avg_count;

public average_count {
    get 
    {
        return _avg_count;
    }

    internal set
    {
        return _avg_count;
    }
}

Where _avg_count is updated when in the time and count set accessors?

It seems like the first is easier to read but may be slower if average_count is accessed often. Will the compiler optimizations make the difference insignificant?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

嗫嚅 2024-09-23 00:03:30

即时执行会产生更具可读性的代码。预先计算可能会提高性能,但只有在以下情况下才应该这样做:(a) 有必要,并且 (b) 您已经进行了分析并且它会产生影响。

最重要的是,只有在绝对必要时才应该牺牲可读性和可维护性来提高性能。

Doing it on the fly results in more readable code. Precalculating may improve performance, but you should only do this if (a) it's necessary and (b) you've profiled and it makes a difference.

The bottom line is, readability and maintainability should only be sacrificed for performance when it's absolutely necessary.

梨涡 2024-09-23 00:03:30

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

1.性能

在他的回答中Skilldrick 建议您优先选择可读性强的内容,而不是性能最佳的内容,作为一般规则:

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

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

所以这是你的判断。最好的建议是只要有一点线索就进行分析;如果在您的情况下为了性能而牺牲可读性是合适的,那么您应该这样做。

2.内存使用

0xA3 建议一种相当优雅的方法,提供某种折衷方案:仅根据需要计算值,然后缓存它。

当然,这种方法的缺点是需要更多的内存来维护。 int? 需要的内存量与 int 加上 bool 基本相同(由于对齐问题,实际上可能意味着 64 位)而不仅仅是 40)。如果您有大量此 data 类的实例,并且内存在您的目标平台上是稀缺资源,那么每个实例再增加 32 位来膨胀您的类型可能不是最明智的举动。

3. 可维护性

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

最重要的是,只有您知道自己的具体情况,因此您最有能力决定在这里做什么。

This is such a seemingly simple question, yet it's almost impossible to answer. The reason is that what's "right" depends on a number of factors.

1. Performance

In his answer Skilldrick recommends that you prefer what is readable over what performs best as a general rule:

[R]eadability and maintainability should
only be sacrificed for performance
when it's absolutely necessary.

I would counter this by saying that it is only true in a typical business application, where performance and functionality are two clearly distinguishable features. In certain high-performance software scenarios, this is not such an easy thing to say as performance and functionality may become inextricably linked -- that is, if how well your program accomplishes its task depends on how solidly it performs (this is the case at my current place of employment, a company that does algorithmic trading).

So it's a judgment call on your part. The best advice is to profile whenever you have an inkling; and if it's appropriate to sacrifice readability for performance in your case, then you should do so.

2. Memory Usage

0xA3 suggests a fairly elegant approach providing a compromise of sorts: only calculate the value as needed, and then cache it.

The downside to this approach, of course, is that it requires more memory to maintain. An int? requires essentially the same amount of memory as an int plus a bool (which, due to alignment issues, could actually mean 64 bits instead of just 40). If you have loads and loads of instances of this data class and memory is a scarce resource on the platform you're targeting, bloating your type with another 32 bits per instance might not be the smartest move.

3. Maintainability

That said, I do agree in general with what others have said that, all else being equal, you're better off erring on the side of readability so that you can understand your code if and when you revisit it in the future. However, none of the various approaches to this problem is particularly complicated, either.

The bottom line is that only you know your exact circumstances and thus you are in the best position to decide what to do here.

○愚か者の日 2024-09-23 00:03:30

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

It depends how often you will call average_count and how often count and time are modified. For such kind of optimization I would suggest you to use profiler.

执笏见 2024-09-23 00:03:30

如果性能至关重要并且您需要频繁访问该属性,您还有另一个选择:在需要时计算结果,然后缓存结果。该模式看起来像这样:

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

In cases where performance is critical and you need to frequently access the property you have another option as well: Calculating the result when it is needed and then cache the result. The pattern would look like this:

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;
        }
    }
}
等待圉鍢 2024-09-23 00:03:30

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

不要过早优化。

In this simple case, I would definitely implement the calculated version first; then optimize if neccessary. If you store the value, you will also need extra code to recalculate the value if any of the values it depends on, changes, which leads to possibilities for errors.

Don't optimize prematurely.

冷情妓 2024-09-23 00:03:30

我会选择你的第一个选择。这些是内存中的对象,因此您正在执行的操作的计算速度将非常快。此外,如果您为此创建了专用属性(例如,average_count),那么您将必须添加更多代码来在设置器中重新计算时间和计数。

作为旁注(因为您询问的是最佳实践),您应该在 C# 中使用 Pascal 大小写,即首字母大写且无下划线。

I'd go with your first option. These are in-memory objects so the computation on what you're doing is going to be extremely fast. Further, if you created a dedicated property for this (e.g., average_count) then you're going to have to add more code to re-calculate this in the setter for both the time and the count.

As a side note (since you're asking about best practice), you should be using Pascal casing in C# which is initial caps and no underscores.

醉梦枕江山 2024-09-23 00:03:30

<块引用>

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

取决于你认为“重要”的是什么。读取变量相当快。除两个数相当快。事实上,根据 RAM 缓存中的内容,读取变量可能比执行除法花费更长的时间。

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

Will the compiler optimizations make the difference insignificant?

Depends on what you consider "significant". Reading a varaible is rather quick. Dividing two number is rather quick. In fact, depending on what in the RAM cache, reading the variables may take longer than doing the divide.

Use the first method. If it's seems to slow, then consider the second.

怀中猫帐中妖 2024-09-23 00:03:30

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

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();

     }
 }

If you care about thread safety it may be way easier to do the second option rather than the first.

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();

     }
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文