Skip to content

C# 值类型构造函数

🏷️ 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;
    }
}