所以我知道可变结构/值类型在.Net中被认为是“邪恶的”。那为什么可以制作它们呢?什么是可变结构的一些好用途,证明在第一时间将功能添加到CLR是合理的?
答案 0 :(得分:3)
一个很好的问题!就个人而言,我绝对不是粉丝,但是有些像CF / XNA这样的人会发誓他们需要额外的性能,他们可以使用结构来获取(通过)结构,这通常涉及它们是可变的。如果直接在数组中,在公共字段上访问结构,或通过ref
访问结构,则此参数具有一些值,但在许多情况下,您可能会因为通过堆栈空间进行搜索而感到厌烦通过在堆栈上多次复制一个超大的结构。
如果你 使用结构,mutable-or-not,如果你使用序列化,那么完全不可变结构可以是一个真正的问题。部分或“冰棒”不变性在这里可以作为答案,但是对于值类型也诱惑的相同框架往往具有更多的反射限制,使得基于私有/字段的序列化变得棘手。可变性很方便。
以上所有内容都是使用结构来保存实际上更好地归类为对象的内容的示例。 纯值不会改变。永远。所以完全不变性就好了。
作为最后的想法......不是存在的理由,但它会成为极好的采访饲料,并且非常适合哄骗那些不警惕的人。也许它是由担任顾问的人设计的; p
答案 1 :(得分:2)
部分可变的结构对于为这些结构制作构造物是有益的。
所以如果你有类似的东西:
namespace StructEx
{
public struct AStruct
{
public int AnInt { get; internal set; }
}
public class AStructBuilder
{
public AStruct BuildStruct(int anInt)
{
return new AStruct { AnInt = anInt };
}
}
}
在一个程序集中,然后该程序集之外的任何内容都无法修改您构建的结构,但构建它们很容易。
答案 2 :(得分:2)
Mutable结构是.net中唯一可以实现可变值语义的数据类型。像Eric Lippert这样的人讨厌他们,因为他们让编译器编写者的生活更加复杂,但是他们提供的语义通常比参考类型的语义更清晰。
例如,假设一个具有类和结构类型,并具有如下接口:
struct myStruct {public int v; ... other stuff...}; class myClass {public int v; ... other stuff...}; interface ISample { void useStructByValue(myStruct z); void useStructByReference(ref myStruct z); void useClassByValue(myClass z); void useClassByReference(ref myClass z); }
并考虑以下方法:
void test(myStruct struct1, myStruct struct2, myClass class1, myClass class2, ISample munger) { for(int i=0; i < 5; i++) { munger.useStructByValue(struct1); // S1 munger.useStructByReference(ref struct2); // S2 munger.useClassByvalue(class1); // S3 munger.useClassByReference(ref class2); // S4 } }
假设只有那个munger没有使用任何不安全的代码,那么哪些传入的项目的.v
字段会受到四个语句中哪一个的影响?我建议即使没有看到struct1的整个定义,也没有看到munger实现的任何部分,可以告诉struct1.v不会被其中任何一个改变。保证。此外,struct2.v可以由S2改变,但不能由任何其他语句改变。再次,保证。但是,class1.v和class2.v的值可能会被任何语句更改;了解哪些语句可以改变class1.v或class2.v的唯一方法是检查将来,现在或将来实现ISample的每种类型的代码。
换句话说,结构提供了有限但定义明确的语义。类没有。
顺便提一下,由于属性如何工作的限制,不能直接修改结构属性的字段,也不能通过引用传递。即便如此,代码如:
List<myStruct> myList; ... myStruct tempStruct = myList[1]; tempStrict.v = 5; myList[1] = tempStruct;
虽然不是线程安全的,但具有清晰明显的语义。将myClass替换为myStruct,确定性就会消失。如果列表中的每个项目都有一个非共享的myClass实例,那么可以简单地说myList[1].v = 5;
,非常方便。不幸的是,确定列表项是否具有myClass的非共享实例几乎是不可能的。如果某人尝试将myList[1]
中的值复制到myList[0]
时曾说过类似myList[0] = myList[1];
的内容,那么此类声明可能会有效,但会导致后续写入myList[1].v
也会影响myList[0].v
。令人讨厌的令人讨厌的讨厌。结构不会有这个问题。