代码之家  ›  专栏  ›  技术社区  ›  Dan Tao

从外部代码的角度来看,可变结构是不可变的,那又如何呢?

  •  0
  • Dan Tao  · 技术社区  · 14 年前

    更新 :在发布此问题后,我突然想到,这种想法的主要缺点只是,这样的类型很容易使用不当。也就是说,必须以非常具体的方式使用该类型才能获得任何好处。我最初想到的是这样使用的东西(坚持 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. 快速初始化

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

    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 几乎可以肯定的是,在这种情况下,这种方法的成本不足以让人认为是值得的。这只是一个例证。

    但我的想法是,这实现了我的三个目标,而最明显的替代方法是无法实现的。明确地:

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

    是的,这个想法将有效地导致一个内部可变的值类型。然而,就任何外部代码所知(在我看来),它会 不变,同时带来一些性能优势(同样,我意识到上面的示例 适当地使用这个想法;我所说的“性能优势”取决于计算的实际成本是否足以保证缓存)。

    3 回复  |  直到 14 年前
        1
  •  1
  •   Gabe Timothy Khouri    14 年前

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

        2
  •  1
  •   supercat    14 年前

    您所描述的可能会起到一定的作用,但有一个重要的警告:计算速度慢的结果在计算时可能会被存储,也可能不会被存储。例如,如果对枚举返回的结构执行计算,则结果将存储在“临时”结构中,并将被丢弃,而不是传播回数据结构中存储的结构中。

        3
  •  0
  •   user180326 user180326    14 年前

    这看起来像一个 Future . 有人提到在C#4.0并行扩展中更好地支持未来。(比如在与正常工作并行的另一个内核/线程上计算它们)。