c#结构可变性:布尔值可以改变吗?



我正在将以前的class更改为Struct,作为Unity编辑器补丁的一部分。我读过很多关于使用结构的建议:"不允许可变结构",因为糟糕的复制行为会导致修改副本,而且很难跟踪。据我所知,这是基于堆栈且没有数据开销的结果。

但是,我想用具体的案例来澄清这一点。布尔值是否可以在结构体中改变,因为数据大小永远不会改变?特定的布尔属性理论上可以随频率修改,所以如果它可能导致内存问题,我将不得不实现其他一些方法来跟踪其他地方的参数。

额外注释,以防意外的相关性:

  • 类有三个属性,其中一个是布尔值。
  • 这两个非布尔属性是不可变的。

"可变结构是邪恶的"这句咒语;这本身并没有反映出真正的问题是什么。更准确的版本应该是"在不可变位置的秘密可变结构是邪恶的"。这些位置通常包括非ref属性或方法返回、readonly字段或任何非变量表达式。

人们通常感到惊讶的是,他们忘记了结构和类是不同的,并且必须以不同的方式处理。说结构是基于堆栈的是不正确的,相反,它们是"无间接性的"。类的实例有自己的标识——它们通过引用间接传递。结构体的实例是;它们没有自己的标识符,它们的标识符是存储它们的变量的标识符

与另一个答案相反,这段代码是不是问题:

myObject.MyStructProperty.MyBoolfield = true;

编译器在这种情况下引发CS1612,保护您免受最常见的错误源的影响。它理解这是(在属性突变的情况下,很可能)一个临时值的无用突变,并且自c# 7以来,使用ref属性无论如何都可以给你想要的。

实际问题是方法。c#在版本8之前没有readonly方法,这意味着编译器总是必须假设一个方法可能会修改值(必须为readonly字段创建防御性副本),但是这些方法在所有情况下都必须是可调用的,因为它们也可能只对它们的返回值有用。

myObject.MyStructProperty.FlipBoolfield();

编译器无法警告您这种情况,因为它不知道FlipBoolfield秘密地改变了的值。如果MyStructProperty不是ref,则突变发生在该值的临时副本上,并且更改将丢失。

总而言之,简单的不要通过方法改变结构体。把所有的struct方法标记为readonly,但是如果你愿意的话,保留可变的属性和字段。

因为这是在Unity的环境中,引擎实际上在任何地方都使用了许多可变结构体(和字段),所以你不会冒着遇到错误的风险。

简单地说,这很好:

public struct DayDuration
{
public int Days;
}

这是不行的:

public struct DayDuration
{
public int Days;
// secret struct mutation
public void AddDays(int count)
{
Days += count;
}
}

public struct DayDuration
{
public int Days;
// explicit ref parameter
public static void AddDays(ref DayDuration duration, int count)
{
duration.Days += count;
}
}

或者更好:

public struct DayDuration
{
public int Days;
}
public static class DayDurationExtensions
{
// amazing to use and warns against non-mutable locations
public static void AddDays(this ref DayDuration duration, int count)
{
duration.Days += count;
}
}

你似乎也对"管理费用"感到特别困惑。结构/类。由于结构体缺乏额外的间接性,访问它们的速度更快,因为CPU不需要遍历引用,并且根本不需要调用GC。但是要注意,在没有ref的情况下,将struct实例(value)分配给不同的位置将复制所有数据,因此您不会从中获得任何内存优化。

数据大小的改变不是结构体不应该改变的原因。

主要原因是可变结构体是按值而不是按引用复制的。典型的例子是使用结构体作为属性:

myObject.MyStructProperty.MyBoolfield = true;

MyStructProperty将返回一个副本,只有这个副本将被改变,而不是myObject中的实际字段。

如果结构体很小,只需创建一个副本,这是完全安全的,并且创建结构体非常便宜。如果只有三个属性,这种情况很可能发生。你对性能的关注很可能是毫无根据的,在对性能做出假设之前一定要进行测量。

如果struct是very大,您可以允许变异,但您需要非常小心,并在任何地方使用引用以避免复制。

相关内容

  • 没有找到相关文章

最新更新