什么是值类型,什么是引用类型
值类型就是直接存储其值
引用类型存储对其值的引用
引用类型:基类为Objcet
值类型:均隐式派生自System.ValueType
- 值类型:
byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。 - 引用类型:
string 和 class ,interface,array ,delegate统称为引用类型。
区别
- 值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
- 引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
对象的传递
将值类型的变量赋值给另一个变量(或者作为参数传递),会执行一次值复制。将引用类型的变量赋值给另一个引用类型的变量,它复制的值是引用对象的内存地址,因此赋值后就会多个变量指向同一个引用对象实例。
代码示例
int v1 = 0;
int v2 = v1;
v2 = 100;
Console.WriteLine("v1=" + v1); //输出:v1=0
Console.WriteLine("v2=" + v2); //输出:v2=100
User u1=new User();
u1.Age = 0;
User u2 = u1;
u2.Age = 100;
Console.WriteLine("u1.Age=" + u1.Age); //输出:u1.Age=100
Console.WriteLine("u2.Age=" + u2.Age); //输出:u2.Age=100,因为u1/u2指向同一个对象
按值传递
- 对于引用类型(int a):传递的是变量a的值 拷贝的副本,因此原本的a值并没有改变。
- 对于引用类型(User user):传递的是变量user的引用地址(User对象示例的内存地址)拷贝副本,因此他们操作的都是同一个User对象实例。
private void DoTest(int a)
{
a *= 2;
}
private void DoUserTest(User user)
{
user.Age *= 2;
}
[NUnit.Framework.Test]
public void DoParaTest()
{
int a = 10;
DoTest(a);
Console.WriteLine("a=" + a); //输出:a=10
User user = new User();
user.Age = 10;
DoUserTest(user);
Console.WriteLine("user.Age=" + user.Age); //输出:user.Age=20
}
按引用传递
按引用传递的连个主要关键字:out和ref,不管是值类型还是引用类型,按引用传递的效果都是一样的,都是传递的是地址的引用(类型c++的指针)。out和ref告诉编辑器方法传递的是参数地址,而不时参数本身。
private void DoTest( ref int a)
{
a *= 2;
}
private void DoUserTest(ref User user)
{
user.Age *= 2;
}
[NUnit.Framework.Test]
public void DoParaTest()
{
int a = 10;
DoTest(ref a);
Console.WriteLine("a=" + a); //输出:a=20 ,a的值改变了
User user = new User();
user.Age = 10;
DoUserTest(ref user);
Console.WriteLine("user.Age=" + user.Age); //输出:user.Age=20
}
out和ref的主要异同
- out和ref都知识编译器传递的参数地址,在行为上都是相同的。
- 他们的使用机制稍微有不同,ref要求参数在使用之前要显式初始化,out要在方法内部初始化。
out和ref不可以重载,就是不能定义Method(ref int a)和Method(out int a)这样的重载,从编译角度来看,二者的实质是相同的,只有使用时有区别。