在这里的SO讨论之后,我已经多次看到这样的评论,即可变结构体是“邪恶的”(就像在这个问题的答案中)。

c#中的可变性和结构的实际问题是什么?


当前回答

结构是值类型,这意味着它们在传递时被复制。

所以如果你改变了一份副本,你只是改变了那份副本,而不是原件,也不是周围可能存在的其他副本。

如果你的struct是不可变的,那么所有通过值传递的自动副本都是相同的。

如果你想要改变它,你必须有意识地用修改过的数据创建一个结构的新实例。(非副本)

其他回答

就我个人而言,当我看代码时,下面的代码看起来相当笨拙:

data.value.set ( data.value.get () + 1 ) ;

而不是简单地

数据.值++ ;或数据值 = 数据值 + 1 ;

数据封装在传递类时非常有用,并且您希望确保以受控的方式修改值。然而,当你拥有公共的set和get函数,它们所做的仅仅是将值设置为传递进来的值时,这比简单地传递公共数据结构有什么改进呢?

当我在类中创建私有结构时,我创建了该结构来将一组变量组织到一个组中。我希望能够在类范围内修改该结构,而不是获得该结构的副本并创建新实例。

对我来说,这阻止了有效使用用于组织公共变量的结构,如果我想要访问控制,我会使用类。

具有公共可变字段或属性的结构并不邪恶。

Struct methods (as distinct from property setters) which mutate "this" are somewhat evil, only because .net doesn't provide a means of distinguishing them from methods which do not. Struct methods that do not mutate "this" should be invokable even on read-only structs without any need for defensive copying. Methods which do mutate "this" should not be invokable at all on read-only structs. Since .net doesn't want to forbid struct methods that don't modify "this" from being invoked on read-only structs, but doesn't want to allow read-only structs to be mutated, it defensively copies structs in read-only contexts, arguably getting the worst of both worlds.

尽管在只读上下文中处理自突变方法存在问题,但是,可变结构通常提供的语义要比可变类类型优越得多。考虑以下三个方法签名:

struct PointyStruct {public int x,y,z;};
class PointyClass {public int x,y,z;};

void Method1(PointyStruct foo);
void Method2(ref PointyStruct foo);
void Method3(PointyClass foo);

对于每种方法,请回答以下问题:

假设该方法没有使用任何“不安全”代码,它会修改foo吗? 如果在调用方法之前没有对'foo'的外部引用,那么在调用方法之后是否可以存在外部引用?

答案:

问题1: Method1(): no(意图明确) Method2(): yes(明确的意图) Method3(): yes(不确定意图) 问题2: Method1():没有 Method2(): no(除非不安全) Method3():是的

Method1 can't modify foo, and never gets a reference. Method2 gets a short-lived reference to foo, which it can use modify the fields of foo any number of times, in any order, until it returns, but it can't persist that reference. Before Method2 returns, unless it uses unsafe code, any and all copies that might have been made of its 'foo' reference will have disappeared. Method3, unlike Method2, gets a promiscuously-sharable reference to foo, and there's no telling what it might do with it. It might not change foo at all, it might change foo and then return, or it might give a reference to foo to another thread which might mutate it in some arbitrary way at some arbitrary future time. The only way to limit what Method3 might do to a mutable class object passed into it would be to encapsulate the mutable object into a read-only wrapper, which is ugly and cumbersome.

结构数组提供了美妙的语义。给定矩形类型的RectArray[500],如何将元素123复制到元素456,然后在不影响元素456的情况下,将元素123的宽度设置为555是显而易见的。"RectArray[432] = RectArray[321];…;RectArray[123]。宽度= 555;"。知道Rectangle是一个具有名为Width的整数字段的结构体,就可以知道关于上述语句的所有信息。

Now suppose RectClass was a class with the same fields as Rectangle and one wanted to do the same operations on a RectClassArray[500] of type RectClass. Perhaps the array is supposed to hold 500 pre-initialized immutable references to mutable RectClass objects. in that case, the proper code would be something like "RectClassArray[321].SetBounds(RectClassArray[456]); ...; RectClassArray[321].X = 555;". Perhaps the array is assumed to hold instances that aren't going to change, so the proper code would be more like "RectClassArray[321] = RectClassArray[456]; ...; RectClassArray[321] = New RectClass(RectClassArray[321]); RectClassArray[321].X = 555;" To know what one is supposed to do, one would have to know a lot more both about RectClass (e.g. does it support a copy constructor, a copy-from method, etc.) and the intended usage of the array. Nowhere near as clean as using a struct.

To be sure, there is unfortunately no nice way for any container class other than an array to offer the clean semantics of a struct array. The best one could do, if one wanted a collection to be indexed with e.g. a string, would probably be to offer a generic "ActOnItem" method which would accept a string for the index, a generic parameter, and a delegate which would be passed by reference both the generic parameter and the collection item. That would allow nearly the same semantics as struct arrays, but unless the vb.net and C# people can be pursuaded to offer a nice syntax, the code is going to be clunky-looking even if it is reasonably performance (passing a generic parameter would allow for use of a static delegate and would avoid any need to create any temporary class instances).

就我个人而言,我对Eric Lippert等人对可变值类型的憎恨感到恼火。它们提供了比到处使用的混杂引用类型清晰得多的语义。尽管.net对值类型的支持有一些限制,但在许多情况下,可变值类型比任何其他类型的实体都更适合。

假设您有一个包含1,000,000个结构体的数组。每个结构体都用bid_price、offer_price(可能是小数)等表示权益,这是由c# /VB创建的。

假设数组是在非托管堆中分配的内存块中创建的,以便其他一些本地代码线程能够并发地访问该数组(可能是一些高性能代码进行数学运算)。

想象一下c# /VB代码正在监听价格变化的市场反馈,该代码可能必须访问数组的某些元素(用于任何安全性),然后修改一些价格字段。

想象一下,这个过程以每秒数万次甚至数十万次的速度进行。

让我们面对现实吧,在这种情况下,我们确实希望这些结构体是可变的,它们必须是可变的,因为它们被其他本地代码共享所以创建副本是没有用的;他们需要这样做,因为以这样的速率复制大约120字节的结构是疯狂的,特别是当一个更新实际上可能只影响一两个字节时。

Hugo

Value types basically represents immutable concepts. Fx, it makes no sense to have a mathematical value such as an integer, vector etc. and then be able to modify it. That would be like redefining the meaning of a value. Instead of changing a value type, it makes more sense to assign another unique value. Think about the fact that value types are compared by comparing all the values of its properties. The point is that if the properties are the same then it is the same universal representation of that value.

正如Konrad所提到的,更改日期也没有意义,因为值代表的是唯一的时间点,而不是具有任何状态或上下文依赖关系的时间对象的实例。

希望这能让你明白。可以肯定的是,它更多的是关于您试图用值类型捕获的概念,而不是实际的细节。

从哪里开始;-p

埃里克·利珀特的博客总是很适合引用:

这是可变的另一个原因 值类型是邪恶的。试着总是 使值类型不可变。

首先,您很容易丢失更改……例如,从列表中获取内容:

Foo foo = list[0];
foo.Name = "abc";

这改变了什么?没有什么有用的…

属性也是一样:

myObj.SomeProperty.Size = 22; // the compiler spots this one

强迫你做:

Bar bar = myObj.SomeProperty;
bar.Size = 22;
myObj.SomeProperty = bar;

不那么关键的是规模问题;可变对象往往有多个属性;然而,如果你有一个包含两个int型,一个string型,一个DateTime型和一个bool型的结构体,你会很快消耗大量内存。使用类,多个调用方可以共享对同一个实例的引用(引用很小)。