0

更新:发布这个问题后,我想到这个想法的主要缺点只是这种类型很容易使用不当。也就是说,必须以非常特定的方式使用该类型才能获得任何好处。我最初想到的是可以像这样使用的东西(SquareRootStruct为了保持一致性,坚持使用原始问题中的示例):

class SomeClass
{
    SquareRootStruct _key;

    public SomeClass(int value)
    {
        _key = new SquareRootStruct(value);
    }

    public double SquareRoot
    {
        // External code would have to access THIS property for caching
        // to provide any benefit.
        get { return _key.SquareRoot; }
    }

    public SquareRootStruct GetCopyOfKey()
    {
        // If _key has cached its calculation, then its copy will carry
        // the cached results with it.
        return _key;
    }
}

// elsewhere in the code...
var myObject = new SomeClass();

// If I do THIS, only a COPY of myObject's struct is caching a calculation,
// which buys me nothing.
double x = myObject.GetCopyOfKey().SquareRoot;

// So I would need to do this in order to get the benefit (which is,
// admittedly, confusing)...
double y = myObject.SquareRoot;

因此,考虑到这很容易出错,我倾向于认为里德(在他的评论中)可能是对的,这对于一个班级来说更有意义。


假设我有一个struct我想要具有以下特征的产品:

  1. 外部角度来看是不可变的
  2. 快速初始化
  3. 某些属性的延迟计算(和缓存)

显然,第三个特征意味着可变性,我们都知道这是不好的(假设“不要制作可变值类型!”的口头禅已经深入到我们的脑海中)。但在我看来,只要可变部分仅在类型本身内部可见,并且从外部代码的角度来看,值总是相同的,这将是可以接受的。

这是我正在谈论的一个例子:

struct SquareRootStruct : IEquatable<SquareRootStruct>
{
    readonly int m_value;  // This will never change.

    double m_sqrt;         // This will be calculated on the first property
                           // access, and thereafter never change (so it will
                           // appear immutable to external code).

    bool m_sqrtCalculated; // This flag will never be visible
                           // to external code.

    public SquareRootStruct(int value) : this()
    {
        m_value = value;
    }

    public int Value
    {
        get { return m_value; }
    }

    public double SquareRoot
    {
        if (!m_sqrtCalculated)
        {
            m_sqrt = Math.Sqrt((double)m_value);
            m_sqrtCalculated = true;
        }

        return m_sqrt;
    }

    public bool Equals(SquareRootStruct other)
    {
        return m_value == other.m_value;
    }

    public override bool Equals(object obj)
    {
        return obj is SquareRootStruct && Equals((SquareRootStruct)obj);
    }

    public override int GetHashCode()
    {
        return m_value;
    }
}

现在,显然这是一个微不足道的例子,因为Math.Sqrt在这种情况下几乎可以肯定认为这种方法值得考虑。这只是用于说明目的的示例。

但我的想法是,这实现了我的三个目标,而最明显的替代方法却无法实现。具体来说:

  • 可以在类型的构造函数中执行计算;但这可能会达不到上述第二个目标(快速初始化)。
  • 可以对每个属性访问执行计算;但这可能会达不到上述第三个目标(缓存计算结果以供将来访问)。

所以是的,这个想法将有效地导致内部可变的值类型。然而,就任何外部代码而言(如我所见),它看起来是不可变的,同时带来了一些性能优势(再次,我意识到上面的示例适合使用这个想法;“性能好处”我所说的将取决于计算实际上是否足够昂贵以保证缓存)。

我是否遗漏了什么,或者这实际上是一个值得的想法?

4

3 回答 3

1

拥有内部使用突变的不可变数据结构并没有错(只要线程安全得到很好的宣传)。只是结构总是被复制,所以你不能对突变做太多事情。这在 C/C++ 中不是问题,因为结构通常通过引用传递,而在 C# 中很少通过引用传递结构。由于很难推断按值传递的结构,因此在 C# 中不鼓励使用可变结构。

于 2010-09-27T19:51:50.080 回答
1

您所描述的内容可能会有所帮助,但有一个重要的警告:缓慢计算的结果在计算时可能会或可能不会被存储。例如,如果对枚举返回的结构执行计算,则结果将存储在“临时”结构中,并将被丢弃而不是传播回存储在数据结构中的结构。

于 2010-09-27T19:55:00.187 回答
0

这看起来类似于Future。有人提到在 C# 4.0 并行扩展中对 Futures 的更好支持。(就像在与您的正常工作并行的另一个核心/线程上计算它们一样)。

于 2010-09-27T19:31:09.630 回答