我理解在参考类型上使用值类型的决定应该基于语义,而不是性能。我不明白为什么值类型可以合法地包含引用类型成员?这有几个原因:
首先,我们不应该构建一个需要构造函数的结构。
public struct MyStruct
{
public Person p;
// public Person p = new Person(); // error: cannot have instance field initializers in structs
MyStruct(Person p)
{
p = new Person();
}
}
其次,由于值类型语义:
MyStruct someVariable;
someVariable.p.Age = 2; // NullReferenceException
编译器不允许我在声明中初始化Person
。我必须将它移到构造函数,依赖调用者,或期望NullReferenceException
。这些情况都不是理想的。
.NET Framework是否在值类型中有任何引用类型的示例?我们什么时候应该这样做(如果有的话)?
答案 0 :(得分:20)
值类型的实例永远不会包含引用类型的实例。引用类型对象位于托管堆上的某个位置,值类型对象可能包含引用对象。这样的参考具有固定的大小。这样做很常见 - 例如每次在结构中使用字符串时。
但是,是的,你无法保证struct
中引用类型字段的初始化,因为你无法定义无参数构造函数(如果你用其他语言定义它也不能保证它被调用)比C#)。
你说你应该“不构建struct
来要求构造函数”。我说不然。由于值类型几乎总是是不可变的,因此必须使用构造函数(很可能通过工厂到私有构造函数)。否则它将永远不会有任何有趣的内容。
使用构造函数。构造函数很好。
如果您不想传入Person
的实例来初始化p
,则可以通过属性使用延迟初始化。 (因为显然公共领域p
仅用于演示,对吧?对吗?)
public struct MyStruct
{
public MyStruct(Person p)
{
this.p = p;
}
private Person p;
public Person Person
{
get
{
if (p == null)
{
p = new Person(…); // see comment below about struct immutability
}
return p;
}
}
// ^ in most other cases, this would be a typical use case for Lazy<T>;
// but due to structs' default constructor, we *always* need the null check.
}
答案 1 :(得分:3)
对于包含类类型字段的结构,有两种主要的有用方案:
(*)语句structVar = new structType(whatever);
将创建structType
的新实例,将其传递给构造函数,然后通过复制所有公共和私有字段来变异structVar
新实例进入structVar
;完成后,新实例将被丢弃。因此,所有结构域都是可变的,即使它们“假装”不是这样;假装它们是不可变的,除非知道structVar = new structType(whatever);
实际实现的方式永远不会造成问题,否则它可能会变得狡猾。
(**)在某些情况下,结构会表现得更好;课程在其他课程中表现更好。通常,在期望它们表现更好的情况下,选择所谓的“不可变”结构,而在它们的语义与类的语义不同的极端情况下,预计不会产生问题。
有些人喜欢假装结构类似于类,但更有效率,并且不喜欢使用结构来利用它们不是类的事实。这些人可能只倾向于使用上面的情景(2)。场景#1对于可变结构非常有用,特别是像String
这样的类型,它们基本上表现为值。
答案 2 :(得分:0)
我想补充一下Marc的回答,但我有太多话要说评论。
如果你看一下C#规范,它会说结构构造函数:
使用new运算符调用struct构造函数,但确实如此 并不意味着正在分配内存。而不是动态的 分配一个对象并返回一个对它的引用,一个结构 构造函数只返回结构值本身(通常在一个 堆栈上的临时位置),然后将该值复制为 必要的。
(您可以在中找到规格的副本
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC#\Specifications\1033
)
因此,结构构造函数本质上与类构造函数不同。
除此之外,结构预计会按值复制,因此:
对于结构体,每个变量都有自己的数据副本,并且 对一方的操作不可能影响另一方。
每当我在结构中看到引用类型时,它就是一个字符串。这有效,因为字符串是不可变的。我猜你的Person
对象不是不可变的,并且可能会引入非常奇怪和严重的错误,因为它与结构的预期行为有所不同。
话虽如此,您使用结构的构造函数看到的错误可能是您的公共字段p
与您的参数p
具有相同的名称而未提及结构p
为this.p
,或者您错过了关键字struct
。