Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.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#_Value Type_Reference Type - Fatal编程技术网

C# 值类型何时包含引用类型?

C# 值类型何时包含引用类型?,c#,value-type,reference-type,C#,Value Type,Reference Type,我理解在引用类型之上使用值类型的决定应该基于语义,而不是性能。我不明白为什么值类型可以合法地包含引用类型成员?这有几个原因: 首先,我们不应该构建一个需要构造函数的结构 public struct MyStruct { public Person p; // public Person p = new Person(); // error: cannot have instance field initializers in structs MyStruct(Perso

我理解在引用类型之上使用值类型的决定应该基于语义,而不是性能。我不明白为什么值类型可以合法地包含引用类型成员?这有几个原因:

首先,我们不应该构建一个需要构造函数的结构

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在值类型中是否有引用类型的示例?我们什么时候应该这样做(如果有)?

值类型的实例从不包含引用类型的实例。引用类型化对象位于托管堆的某个位置,值类型化对象可能包含对该对象的引用。这样的引用具有固定的大小。这是非常常见的——例如,每次在结构中使用字符串时

但是,是的,您不能保证在
结构中初始化引用类型字段,因为您不能定义无参数构造函数(如果您用C#以外的语言定义它,您也不能保证它会被调用)

你说你不应该“构建一个
struct
来要求一个构造函数”。我不这么说。由于值类型几乎总是不可变的,所以必须使用构造函数(很可能是通过工厂到私有构造函数)。否则它将永远不会有任何有趣的内容

使用构造函数。构造器很好

如果不想传入
Person
的实例来初始化
p
,可以通过属性使用延迟初始化。(因为很明显公共字段
p
只是为了演示,对吗?对吗?)

public struct MyStruct
{
公共MyStruct(个人p)
{
这个,p=p;
}
私人p;
公众人物
{
得到
{
if(p==null)
{
p=newperson(…);//请参阅下面关于结构不变性的注释
}
返回p;
}
}
//^在大多数其他情况下,这将是懒惰的典型用例;
//但是由于structs的默认构造函数,我们*总是*需要空检查。
}

我想补充马克的答案,但我有太多的话要说

如果你看一下C#规范,它会说结构构造函数:

使用新运算符调用结构构造函数,但确实如此 这并不意味着正在分配内存。而不是动态地 分配一个对象并返回对它的引用,一个结构 构造函数只返回结构值本身(通常在 堆栈上的临时位置),然后将此值复制为 必要的

(您可以在
C:\ProgramFiles(x86)\Microsoft Visual Studio 10.0\VC\Specifications\1033

因此,结构构造函数本质上不同于类构造函数

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();
    }
}
除此之外,结构应按值复制,因此:

对于结构,每个变量都有自己的数据副本,并且 一个操作不可能影响另一个操作

每当我在结构中看到引用类型时,它都是字符串。这是因为字符串是不可变的。我猜你的
Person
对象不是一成不变的,可能会引入非常奇怪和严重的bug,因为它与结构的预期行为不同


也就是说,您在结构的构造函数中看到的错误可能是,您有一个公共字段
p
,与参数
p
同名,并且没有将结构的
p
作为
this.p
引用,或者缺少关键字
struct

对于包含类类型字段的结构,有两种主要的有用场景:

  • 该结构包含对不可变对象的可能可变引用(`String`是目前最常见的)。对不可变对象的引用将表现为可空值类型和正常值类型之间的交叉;它没有前者的“Value”和“HasValue”属性,但可能(和默认)的值为null。请注意,如果通过属性访问字段,则当字段为null时,该属性可能会返回非null默认值,但不应修改字段本身。
  • 结构持有对可能可变对象的“不可变”引用,并用于包装对象或其内容`枚举器`可能是使用此模式的最常见的结构。让结构字段假装不可变是一种不可靠的构造(*),但在某些情况下,它可以很好地工作。在应用此模式的大多数情况下,结构的行为本质上与类的行为相似,只是性能更好(**)。 (*)语句
    structVar=newstructType(无论什么)
    将创建一个新的
    structType
    实例,将其传递给构造函数,然后通过将所有公共和私有字段从该新实例复制到
    structVar
    中来对
    structVar
    进行变异;完成后,新实例将被丢弃。因此,所有结构字段都是可变的,即使它们“假装”是可变的;假装它们是不可变的可能是不可靠的,除非你知道方法
    structVar=newstructType(无论什么)不会带来问题

    (**)结构在某些情况下性能更好;其他班级的表现会更好。通常,在预期性能更好的情况下,所谓的“不可变”结构被选择在类之上,并且在某些情况下