C#学习( ref和out、堆和栈、枚举)

一、值类型和引用类型

1、值类型直接存储其值(存储数据的实际值),而引用类型存储对其值的引用(存储的是它们数据的引用)。

引用类型:基类为Objcet 

c# 中所有的类、结构体和接口类型都隐式的继承Object类

值类型:均隐式派生自System.ValueType

C#学习( ref和out、堆和栈、枚举)_第1张图片

2、值和引用类型存储

  • 值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
  • 引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
  • 值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
  • 引用类型的对象总是在进程堆中分配(动态分配)。

3、引用类型

数组(派生于System.Array)

类:class(派生于System.Object);

4、值类型

C#的所有值类型均隐式派生自System.ValueType:结构体:struct(直接派生于System.ValueType)

5、装箱和拆箱

1、装箱:装箱就是将值类型转化为引用类型的过程。

装箱过程:

  • 在托管堆中分配好内存,分配的内存量是值类型的各个字段需要的内存量加上托管堆上所以对象的两个额外成员(类型对象指针,同步块索引)需要的内存量
  • 值类型的字段复制到新分配的堆内存中
  • 返回对象的地址,这个地址就是这个对象的引用

2、拆箱:将已装箱的值类型实例转化成值类型的过程。

 

二、ref和out

通常向方法传递一个实参时,对应的参数(形参)会用实参的一个副本来初始化。

1、ref

如果为一个参数(形参)附加一个ref关键字作为前缀,该参数就会成为一个实参的一个别名(或对实参的一个引用),而不再是一个实参一个副本。   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 6;
            int b = 66;
            Fun(ref a,ref b);
            Console.WriteLine("a:{0},b:{1}", a, b);//输出:72和6说明传入Fun方法是a和b的引用
        }
        static void Fun(ref int a, ref int b) {
            a = a+b;  //72,说明Main方法的a和b的值传进来了
            b = 6;
        }
    }
}

                       

通过以上例子,形参的操作改变了实参的值。 ref修饰的是引用类型,而不是值类型,也就是说所存储的是地址。 

2、out

out与ref的用法类似,不过out可以向方法传递一个未赋初值的实参。         

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a=100;
            int b;
            Fun(out a, out b);
            Console.WriteLine("a:{0},b:{1}", a, b);//输出:3和1说明out参数传递进去的是a和b的引用,输出3说明a的参数值没有传入Fun方法中
        }
        static void Fun(out int a, out int b)
        {
            a = 1+2;
            b = 1;
        }
    }
}

3、两者区别:ref有进有出,out无进有出

三、堆和栈

        计算机使用内存来容纳要执行的程序以及这些程序要使用的数据。操作系统和运行时通常将用于容纳数据的内存划分为两个独立的区域,每个区域都采用不同的方式进行管理。这两个区域通常称为堆和栈。

参考:https://blog.csdn.net/taoerit/article/details/53420684

1、调用一个方法时,它的参数以及它的局部变量需要内存总是从栈中获取。方法结束后,为参数和局部变量分配的内存将自动归还栈。

使用new关键字来创建的一个对象,构造对象所需要的内存总是从堆中获取。

2、栈内存就像是一系列堆叠越高的箱子,调用方法时,它的每个参数(或局部变量)都被放进一个箱子,并将这个箱子放到栈的最顶部。方法结束之后,方法的所有箱子都会从栈中移除。

堆内存则像散布在房间里的一大堆箱子。每个箱子都有一个标签,它标记了这个箱子是否正在使用。创建一个新对象时,“运行时”会查找一个空箱子,并把它分配给对象。当最后一个引用消失,运行时就将箱子标记为“未使用”。将来某个时候,会清除箱子里的东西,使之能被真正重用。

3、栈和堆中主要放置了四种类型的数据:值类型(Value Type),引用类型(Reference Type),指针(Pointer),指令(Instruction)。

(1)值类型:

bool 、byte 、char 、decimal 、double 、enum 、float 、int 、long 、sbyte 、short 、struct 、uint 、ulong 、ushort

(2)引用类型

class 、 interface 、delegate 、object 、string

(3)指针

(4)指令

四、枚举

        枚举是由一组特定常量构成的一组数据结构,是值类型的一种特殊形式,当需要一个由指定常量集合组成的数据类型时,使用枚举类型。枚举声明可以显式地声明 byte、sbyte、short、ushort、int、uint、long 或 ulong 类型作为对应的基础类型。没有显式地声明基础类型的枚举声明意味着所对应的基础类型是 int。

1、格式

enum { enumeration list }; 

enum_name指定枚举的类型名称,enumeration list是一个用逗号分隔的标识符列表。枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0.

枚举的取值类型:byte、sbyte、short、ushort、int、uint、long 、ulong

2、注意

枚举类型只针对字符串,对于索引,无意义。
常量的集合,这些常量只能取值,不能赋值。
用常量表示所引用的字符串,这样可以省去重复写入长字符串 。

3、举例

例如,假设你需要定义一个变量,其值表示一周内的某一天。 该变量只会存储七个有意义的值。 若要定义这些值,可以使用枚举类型,该类型是使用 enum 关键字声明的。

声明:

enum Day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
enum Month : byte { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

使用枚举

Day today = Day.Monday;
int dayNumber =(int)today;
Console.WriteLine("{0} is day number #{1}.", today, dayNumber);

Month thisMonth = Month.Dec;
byte monthNumber = (byte)thisMonth;
Console.WriteLine("{0} is month number #{1}.", thisMonth, monthNumber);

// Output:
// Monday is day number #1.
// Dec is month number #11.

如果未为枚举器列表中的元素指定值,则值将自动按 1 递增。在上例中,Day.Sunday 的值为 0,Day.Monday 的值为 1,依此类推。

你可能感兴趣的:(C#)