Skip to content
标签
欢迎扫码关注公众号

C# 值类型构造函数

创建一个值类型 Point,一个引用类型 Rectangle

csharp
namespace StructCtorSample
{
    class Program
    {
        static void Main(string args)
        {
            Rectangle rc = new Rectangle();
            Point p = new Point();
        }
    }
    struct Point
    {
        public int m_x, m_y;
    }
    class Rectangle
    {
        public Point m_topLeft, mbottomRight;
    }
}

编译后的 IL 代码如下:

点击查看 IL 代码
cs
.class private auto ansi beforefieldinit StructCtorSample.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string args) cil managed
  {
    .entrypoint
    // 代码大小       16 (0x10)
    .maxstack  1
    .locals init ([0] class StructCtorSample.Rectangle rc,
             [1] valuetype StructCtorSample.Point p)
    IL_0000:  nop
    IL_0001:  newobj     instance void StructCtorSample.Rectangle::.ctor()
    IL_0006:  stloc.0
    IL_0007:  ldloca.s   p
    IL_0009:  initobj    StructCtorSample.Point
    IL_000f:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // 代码大小       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class StructCtorSample.Program

.class private sequential ansi sealed beforefieldinit StructCtorSample.Point
       extends [mscorlib]System.ValueType
{
  .field public int32 m_x
  .field public int32 m_y
} // end of class StructCtorSample.Point

.class private auto ansi beforefieldinit StructCtorSample.Rectangle
       extends [mscorlib]System.Object
{
  .field public valuetype StructCtorSample.Point m_topLeft
  .field public valuetype StructCtorSample.Point mbottomRight
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // 代码大小       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Rectangle::.ctor

} // end of class StructCtorSample.Rectangle

注意看一下两个变量的创建指令:

cs
IL_0001:  newobj     instance void StructCtorSample.Rectangle::.ctor()
IL_0009:  initobj    StructCtorSample.Point

Rectangle 实例是使用 newobj 指令;Point 实例则是只使用了 initobj 指令;

可以看一下 读《你必须知道的 .NET》IL 指令笔记 中对这两个指令的描述。

再看一下 Point 类和 Rectangle 类的实例代码,可以发现 Point 类里根本就没有构造函数,而 Rectangle 类则自动生成了 ctor 方法。

那么我们显示的创建构造函数呢?编译器会不会就可以自动调用了呢?

修改一下 Point 类型追加一个无参构造函数:

csharp
struct Point
{
    public int m_x, m_y;
    Point()
    {
        m_x = m_y = 5;
    }
}

结果却是编译出错了:

结构不能包含显式的无参数构造函数

原因是:C# 不允许值类型自定义无参构造函数

但其实 CLR 中是允许的。那 C# 为什么不允许呢?当然是有原因的啦。

还是回到创建变量时的指令,值类型是使用 initobj 指令创建的,该指令只完成值类型的初始化,并不调用构造函数,也就是说即使值类型有构造函数,也不会自动调用。

就像上面 Point 的无参构造函数,我们预想的是 xy 值为 5,若结果创建对象后还是 0,会让程序猿感觉很困惑,所以C# 默认禁止了值类型创建无参构造函数。

但 C# 是允许创建有参构造函数的,但是有一点要注意,必须在构造函数中为所有的字段设定值,否则编译时会报如下错误。

在控制返回到调用方之前,字段“StructCtorSample.Point.m_y”必须完全赋值

值类型有参构造函数的示例:

csharp
struct Point
{
    public int m_x, m_y;
    Point(int x, int y)
    {
        m_x = x;
        m_y = y;
    }
}

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.