Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 那么';从外部代码的角度来看,它是不可变的?_C#_.net_Struct_Immutability_Mutable - Fatal编程技术网

C# 那么';从外部代码的角度来看,它是不可变的?

C# 那么';从外部代码的角度来看,它是不可变的?,c#,.net,struct,immutability,mutable,C#,.net,Struct,Immutability,Mutable,更新:在发布了这个问题后,我突然想到,这个想法的主要缺点就是这种类型很容易被不当使用。也就是说,必须以非常具体的方式使用该类型才能获得任何好处。我最初想到的是这样使用的东西(为了保持一致性,坚持使用原始问题中的SquareRootStruct示例): 所以,考虑到这很容易出错,我倾向于认为里德的观点(在他的评论中)可能是正确的,这对于一个班级来说更有意义 假设我有一个struct,我希望它具有以下特征: 从外部有利位置看是不变的 快速初始化 某些属性的延迟计算(和缓存) 显然,第三个特征意味着

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

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


假设我有一个
struct
,我希望它具有以下特征:

  • 从外部有利位置看是不变的
  • 快速初始化
  • 某些属性的延迟计算(和缓存)
  • 显然,第三个特征意味着易变性,这一点我们都知道是不好的(假设“不要制造易变的值类型!”这句咒语已经深入我们的头脑)。但在我看来,只要可变部分仅对类型本身内部可见,并且从外部代码的角度来看,值总是相同的,那么这是可以接受的

    下面是我所说的一个例子:

    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;
        }
    }
    
    struct SquareRootStruct:IEquatable
    {
    只读int m_value;//这永远不会更改。
    double m_sqrt;//这将在第一个属性上计算
    //访问权限,此后再也不会更改(因此它将
    //对外部代码不可变)。
    bool m_sqrtCalculated;//此标志将永远不可见
    //外部代码。
    public SquareRootStruct(int值):this()
    {
    m_值=值;
    }
    公共整数值
    {
    获取{返回m_值;}
    }
    公共双平方根
    {
    如果(!m_sqrtCalculated)
    {
    m_sqrt=数学sqrt((双)m_值);
    m_sqrtCalculated=真;
    }
    返回m_sqrt;
    }
    公共布尔等于(SquareRootStruct其他)
    {
    返回m_值==其他.m_值;
    }
    公共覆盖布尔等于(对象对象对象)
    {
    返回obj为SquareRootStruct&&Equals((SquareRootStruct)obj);
    }
    公共覆盖int GetHashCode()
    {
    返回m_值;
    }
    }
    

    现在,显然这是一个<强>平凡的例子,如<代码>数学。Sqrt < /COD>几乎不必花费太多代价,认为这种方法在这种情况下是值得的。这只是一个用于说明目的的示例

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

    • 我可以在类型的构造函数中执行计算;但这可能无法实现上述第二个目标(快速初始化)
    • 我可以对每个财产访问进行计算;但这可能无法实现上述第三个目标(缓存计算结果以备将来访问)
    因此,是的,这个想法将有效地导致一个内部可变的值类型。然而,就任何外部代码而言(在我看来),它似乎是不变的,同时也带来了一些性能优势(同样,我意识到上面的示例并不适合使用这种思想;“性能优势”我所说的将取决于计算的实际成本是否足以保证缓存)


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

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

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

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

    我经常这样做。对我来说似乎很合理。我想你必须是某种FP纯粹主义者才会感到不满。@Kirk:是的,但问题变成了,“为什么你希望它是一个结构”而不是一个类?如果你真的在做计算值的“工作”,那么首先它真的是一个结构的候选者吗?@Reed Copsey:问题struct vs.class不是一个类型是否应该有值类型语义的问题,而不是你是否需要做任何内部计算吗?想想
    DateTime
    struct,它可以动态计算
    Year
    Day
    属性。@OxA3:我认为这不是一个好的比较。DateTime只提供这些属性作为帮助程序-就像任何其他进行值类型转换的方法一样。在这种情况下,它不会对自己的内部状态做任何事情来让它工作。易变结构往往有奇怪的副作用,一旦你真的开始需要对它们进行变异(即使是为了惰性),我怀疑它们是否应该改为类。@Dan Tao:BTW-你可能想看看在类内部使用惰性来进行这种类型的操作-它给了你线程安全和非常简单的,
    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;
        }
    }