C#学习笔记(三):值类型、引用类型及参数传递

值类型和引用类型简介

C#中存在两种数据类型,分别是值类型与引用类型,下面我们来看看这两种类型的区别。

值类型主要包括:

  1. 简单类型(如int、float、char等,注意string不是值类型);
  2. 枚举类型(enum);
  3. 结构体类型(struct);

引用类型主要包括:

  1. 类类型(如string);
  2. 数组类型(一维或多维数组);
  3. 接口类型(interface);
  4. 委托类型(delegate);

内存分布

值类型的实例大部分情况下会被存放在线程的堆栈上,由操作系统管理其在内存上的分配和释放。

引用类型的实例都会被存放在托管堆上,由垃圾回收器(GC)管理其在内存上的分配和释放。

 namespace Study
{
class Program
{
static void Main(string[] args)
{
int valueType = ;
string refType = "abc";
}
}
}

上面的示例中:

valueType是值类型,其变量名和值都会被存储到堆栈中。

refType是引用类型,其变量名任然存储在堆栈中,但是其值"abc"是存储在托管堆中的。

引用类型中嵌套定义值类型的情况

 namespace Study
{
class Program
{
static void Main(string[] args)
{
Test test = new Test();
}
} public class Test
{
private int valueType = ;
}
}

上例中的Test作为引用类型被分配到托管堆中,其属性valueType虽然是值类型,但是仍然也会被分配到托管堆中。

总结

  • 值类型继承自ValueType,ValueType继承自Object;引用类型继承自Object;
  • 值类型不由GC控制释放,其作用域结束时会被操作系统自动回收;引用类型需要由GC控制其内存释放;
  • 值类型不能被设置为null;

装箱和拆箱

装箱是指值类型转换为引用类型的过程,拆箱表示将引用类型转换为值类型的过程。

装箱和拆箱堆性能有一定影响,所以我们需要需要尽量避免。

参数传递

形参值方法中定义的参数,实参指实际传递给方法的参数。

值类型参数按值传递

形参作为从实参拷贝的一个副本,对形参的修改不会影响到实参。

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
int num = ;
Func(num); Console.WriteLine("num: " + num); Console.Read();
} private static void Func(int i)
{
i++; Console.WriteLine("i: " + i);
}
}
}

结果如下:

 i:
num:

引用类型参数按值传递

形参作为从实参拷贝的一个副本,但是由于是引用类型记录的是内存地址,所以两个参数实际都指向托管堆中的同一个对象,故对形参的修改会影响到实参。

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.num = ;
Func(t); Console.WriteLine("Main: " + t.num); Console.Read();
} private static void Func(Test test)
{
test.num = ; Console.WriteLine("Func: " + test.num);
}
} public class Test
{
public int num = ;
}
}

结果如下:

 Func:
Main:

string引用类型按值传递的特殊情况

string对象虽然是引用类型,但是由于string类型具备的不变性,在传递参数后实际上在内存中重新拷贝了一份数据。

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
string str = "old string";
Func(str); Console.WriteLine("Main: " + str); Console.Read();
} private static void Func(string s)
{
s += ", new string"; Console.WriteLine("Func: " + s);
}
}
}

结果如下:

 Func: old string, new string
Main: old string

值类型和引用类型参数按引用传递

当需要直接传递参数的引用到函数内部时,通过引用传递参数允许函数成员更改参数的值,并保持该更改,需要使用ref和out关键字来实现:

  1. 使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化;
  2. 使用ref和out时,在方法的参数和执行方法时,都要加ref或out关键字。以满足匹配;
  3. out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候;

下面我们看一个例子:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
int num = ;
FuncInt(ref num);
Console.WriteLine("MainInt: " + num); string str = "old string";
FuncStr(ref str);
Console.WriteLine("MainStr: " + str); Console.Read();
} private static void FuncInt(ref int i)
{
i += ; Console.WriteLine("FuncInt: " + i);
} private static void FuncStr(ref string s)
{
s += ", new string"; Console.WriteLine("FuncStr: " + s);
}
}
}

结果如下:

 FuncInt:
MainInt:
FuncStr: old string, new string
MainStr: old string, new string

如果是out关键字则不能初始化实参,如下:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
int num;
FuncInt(out num);
Console.WriteLine("MainInt: " + num); string str;
FuncStr(out str);
Console.WriteLine("MainStr: " + str); Console.Read();
} private static void FuncInt(out int i)
{
i = ; Console.WriteLine("FuncInt: " + i);
} private static void FuncStr(out string s)
{
s = ", new string"; Console.WriteLine("FuncStr: " + s);
}
}
}

结果如下:

 FuncInt:
MainInt:
FuncStr: , new string
MainStr: , new string
上一篇:前端web应用的组件化(二) 徐飞


下一篇:Jquery 遍历 Table;遍历CheckBox ;遍历Select;全选/全不选