C#编译器是否会为值类型生成默认的构造函数呢?
答案是否定的,C#编译器并不会为值生成默认的构造函数的,这样设计一方面是基于性能方面的考虑,另外一个原因是值类型可以被隐式的创建!这一点可以通过ILDasm工具开验证,值类型确实没有生成默认的构造函数,看下面的代码:
class Program
{
static void Main(string[] args)
{
A a =new A();
}
}
struct A
{
}
有人可能比较奇怪:A a=new A();这行是需要调用结构体的默认构造函数的,如果没有生成默认的构造函数,这里应该同不过编译的,事实正如大家预料的,这行代码可以通过编译的!
这里似乎有自相矛盾的地方啊!先别急,我们先看看Main方法的IL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 10 (0xa)
.maxstack 1
.locals init ([0] valuetype ConsoleApplication1.A a)
IL_0000: nop
IL_0001: ldloca.s a
IL_0003: initobj ConsoleApplication1.A
IL_0009: ret
} // end of method Program::Main
我们看红色的代码行,第一行是把局部变量a入栈,第二行是初始化a,注意使用的是initobj指令,这里调用的是隐式的默认的构造函数,因为每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值!
如果换成引用类型应该使用newobj指令,这时调用C#编译器生成的默认的构造函数了!
实际上这也就是为什么在值类型中的成员声明的同时不能直接初始化的原因,因为C#编译器根本没有为值类型生成默认的构造函数,让他何处初始化呢!
希望这篇文章能够对大家有一点帮助啊!
感谢大家的参与啊!
我在这里统一回复一下大家:
一楼说的对,在C#中,结构体是不允许定义无参的构造函数的,只能定义有参数的构造函数的,这么设计重要是为了避免引起程序员的误解!
比如这样:
Class MyClass
{
public A a;//可能有的人会误认为构造MyClass实例的时候会自动调用A的默认的构造函数,一般有C++的经验的会有这样的想法,这里C#和C++不一样
}
为了避免这种混淆,C#中并不允许在结构体中提供默认的无参构造函数!
关于结构体的用途,可能大家有一定误解!实际上在C#中结构体是很有用的,并不是仅仅为了兼容过去的遗留组件而存在。
下面我详细说说:
大家都知道结构体是值类型的,所以他的实例是存放在栈中的,而且栈的访问速度是比托管堆要快的,但是空间没有堆大!
所以可以把一些比较小的类型定义为结构来提高访问的速度,例如:
struct Point
{
int x;
int y;
}
大家可能想既然访问速度比较快,为什么不全部类型都定义为值类型的呢?道理比较简单,栈的空间并不是很大,不能存放很多的东西,另外在栈中即使是值一样的实例,往往也需要存放两份副本的,比如:
Point p1=new Point();
Point p2=p1;
在栈中就有两份p1的拷贝!
这样本身也比较占空间!
所以只有一些本身比较小的类型适合定义成结构体,大的类型最好还是定义为引用类型,放到空间相对比较大的托管堆中比较好!
再次感谢大家参与讨论啊!谢谢大家!