【问题标题】:Class Using Lengthy Calculations - Reducing Overhead使用冗长计算的类 - 减少开销
【发布时间】:2014-08-20 15:55:09
【问题描述】:

假设我有一个类,它有几个基于计算的属性,甚至可以依赖于共享类成员的存在......举个例子:

Public Class FakeClass
    Public Shared Property Needed As FakeObject
    Public Property Prop1 as Double
    Public Property Prop2 as Double

    Public Function Func1() As Double
        ' Lengthy calculation using some properties, etc
    End Function

    Public Function Func2() As Double
        ' Lengthy calculation involving <Func1>, some properties, etc
    End Function

    Public Function Func3() As Double
        ' Lengthy calculation involving <Func1> and needing <Needed>
    End Function
End Class

并且一个实例化的对象可以在整个代码中被多次调用,所以我不希望每次都重新计算这些值,但是,同时,我也想考虑到代码中某些属性可能已更改的可能性,因此应重新运行方法...

我正在考虑使用以下任何一种方法来做到这一点:

  1. 对于每个方法,创建一个私有布尔值(例如 _RecalcFunc1)和一个私有双精度值(例如 _Func1Val)以使最终方法看起来像:

    Private _RecalcFunc1 As Boolean = True
    Private _Func1Val As Double
    Public Function Func1() As Double
        If _RecalcFunc1 Then
            _Func1Val = ' Lengthy calculation
            _RecalcFunc1 = False
        End If
        Return _Func1Val
    End Function
    

    Public Shared Property Needed As FakeObject = Nothing
    Public Function Func3() As Double
        If Needed Is Nothing Then
            Throw New InvalidOperationException("The ""Needed"" object is necessary to perform this calculation.")
        End If
        ' Lengthy calculation involving <Func1> and needing <Needed>
    End Function
    
  2. 我看到有INotifyPropertyChanged接口之类的东西,但这似乎是通知当前对象之外的对象,而且实现起来似乎很昂贵。

    李>

是否有标准/正确的方法来做到这一点?或者哪种方法/模式最能考虑到这一点?

此外,即使提供的源代码是 VB,我也同样喜欢 VB/C# 解决方案。

谢谢!!!

【问题讨论】:

  • 您可以在属性设置器中设置一个 DataChanged 标志,然后仅在它为 True 时重新计算。 INotifyPropertyChanged 通常用于告诉外部方某个 prop 已更改,例如告诉 BindingList 存储对象的 prop 已更改。
  • 谢谢@Plutonix,非常喜欢我的想法#1??
  • 除了,你不能使用自动属性
  • 是的 - 有道理!! - 谢谢!!!!

标签: c# .net vb.net class oop


【解决方案1】:

您应该为您拥有的每个函数创建一个变量,然后该函数应该只返回与该函数对应的变量。然后在属性设置器方法中,您应该调用一个私有的重新计算方法来进行冗长的计算并将其保存到变量中。

这是显示我的意思的代码:

public class FakeClass
{
    private double func1Result;
    private double func2Result;
    private double func3Result;

    public FakeClass()
    {
        func1Result = Double.MinValue;
        func2Result = Double.MinValue;
        func3Result = Double.MinValue;

        _property1 = Double.MinValue;
        _property2 = Double.MinValue;

        _neededObject = null;
    }

    private double _property1;
    public double Property1
    {
        get { return _property1; }
        set
        {
            if (_property1 != value)
            {
                _property1 = value;
                RecalculateFunc1Result();
            }
        }
    }

    private double _property2;
    public double Property2
    {
        get { return _property2; }
        set
        {
            if (_property2 != value)
            {
                _property2 = value;
                RecalculateFunc2Result();
            }
        }
    }

    private static FakeObject _neededObject;
    public FakeObject Needed
    {
        get { return _neededObject; }
        set
        {
            if (_neededObject != value)
            {
                _neededObject = value;
                RecalculateFunc3Result();
            }
        }
    }

    private double RecalculateFunc1Result()
    {
        // Check to make sure the values are not the default/invalid ones.
        if (_property1 == Double.MinValue ||
            _property2 == Double.MinValue)
        {
            func1Result = Double.MinValue;
            return func1Result;
        }

        //func1Result = Lengthy calculation involing some properties.

        return func1Result;
    }

    private double RecalculateFunc2Result()
    {
        // Check to make sure the values are not the default/invalid ones.
        if (_property1 == Double.MinValue ||
            _property2 == Double.MinValue)
        {
            func2Result = Double.MinValue;
            return func2Result;
        }

        //func2Result = Lengthy calculation involing some properties and 
        //   RecalculateFunc1Result() or func1Result.

        return func2Result;
    }

    private double RecalculateFunc3Result()
    {
        if (Needed == null)
            throw new InvalidOperationException(
                @"The ""Needed"" object is necessary to perform this calculation.");

        //func3Result = Lengthy calculation involving RecalculateFunc1Result()
        //   or func1Result

        return func3Result;
    }
}

【讨论】:

  • 真的,一个非常解释清楚的解决方案,太棒了!!! - 谢谢!! - 唯一的问题/想法...您输入_property1 == Double.MinValue 的逻辑我必须考虑更多,但是重新计算布尔值不是更有意义吗?我看到您在 setter 中重新计算,但只是想知道这是否会在某些地方抓住您?或者你确定这是一种安全的方式吗?
  • 如果您不喜欢_property1 == Double.MinValue 选项,那么您可以让每个double 引用一个double? 引用,然后改为检查_property1 == null。如果在构造函数中使用 null 选项,则必须将所有内容初始化为 null,然后在属性的设置器部分中,您将需要类似 if (value != null &amp;&amp; _neededObject != value)
【解决方案2】:

听起来您的计算值被读取的频率比它们所依赖的值被写入的频率要高得多。在这种情况下,您可以为您的依赖值创建一个实际的实例字段,并*在它们所依赖的任何值的设置器中重新计算它们。

也可能值得考虑使这种类型不可变,而不是可变,尽管这将是一个重大的突破性变化,与之前的选项不同。使类不可变意味着您始终可以在构造对象时急切地计算派生值,而不必担心这些缓存值变得陈旧。然后,当值确实需要更改时,您可以重新创建整个对象的新实例。假设这些值不会经常变化,这可能更可取。

【讨论】:

  • 非常有意义 - 因此,在某种程度上,@Plutonix 在上面的 cmets 中建议的 #1,但首选不可变!
  • 还有一个问题 - 假设这些计算量很大/时间密集,如果它是不可变的,那么在创建对象时执行它们是否仍然有意义?这更多的是我只是好奇...
  • @JohnBus​​tos 如果你很可能需要它,你可以急切地去做。如果你不会这样做是合理的,那么你可以将计算推迟到第一次需要它的时候,当然。
  • ... 只是希望我能借用你的大脑几天!!! - 一如既往,!!!!!!谢谢!!!!!!
猜你喜欢
  • 1970-01-01
  • 2016-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多