【C#学习3】函数、错误处理、堆和栈等

本文深入探讨了C#中的函数重载、委托、递归调用等高级特性,解析了中断模式下的调试技巧,异常处理机制,并介绍了匿名类型、堆栈、垃圾回收器以及值类型与引用类型的区别。

函数

函数的重载

函数名相同,参数不同,这个叫做函数的重载(编译器会根据你传递过来的实参的类型去判定调用哪一个函数)

namespace _008_函数的重载
{
    class Program
    {
        static int MaxValue(params int[] array)
        {
            int maxValue = array[0];
            for (int i = 1; i < array.Length; i++)
            {
                if (array[i]> maxValue)
                {
                    maxValue = array[i];
                }
            }
            return maxValue;
        }
        static double MaxValue(params double[] array)
        {
            double maxValue = array[0];
            for (int i = 1; i < array.Length; i++)
            {
                if (array[i] > maxValue)
                {
                    maxValue = array[i];
                }
            }
            return maxValue;
        }
        static void Main(string[] args)
        {
            int res1 = MaxValue(12, 41, 23);
            double res2 = MaxValue(12.26, 41.2, 55.21);
            Console.WriteLine("{0},{1}",res1, res2);
            Console.ReadKey();
        }
    }
}

委托的声明

委托(delegate)是一种存储函数引用的类型,委托的定义指定了一个返回类型和一个参数列表。当声明该委托类型的变量后,就可以把一个返回类型和参数列表跟委托一样的函数赋值给这个变量。

namespace _009_委托
{
    class Program
    {
        //定义委托需要加上delegate关键字
        //委托的定义不需要函数体
        delegate double myDelegate(double param1, double param2);

        static double Multiply(double param1, double param2)
        {
            return param1 * param2;
        }
        static double Divide(double param1, double param2)
        {
            return param1 / param2;
        }
        static void Main(string[] args)
        {
        	//在声明的时候直接把方法赋予变量来初始化
			//myDelegate de = new myDelegate(Multiply);
            //Console.WriteLine(de(23.2, 4.5));
            
            myDelegate de;  //利用定义的委托声明了一个新的变量
            de = Multiply; //当给一个委托的变量赋值时,返回值类型,以及参数列表必须一样,否则无法赋值
            Console.WriteLine(de(34.2, 64.3));
            de = Divide;
            Console.WriteLine(de(34.2, 64.3));
            Console.ReadKey();
        }
    }
}

函数的递归调用

namespace _010_函数的递归调用
{
    //又叫做斐波那契数列,1,1,2,3,5,8....
    class Program
    {
    	//利用递归方法求阶乘n!(比如5!)
        static int Factorial(int n)
        {
            if (n == 1) return 1;
            return n * Factorial(n - 1);
        }
        
        static int F(int n)
        {
            //这两个是函数终止递归的条件
            if (n == 0) return 2;
            if (n == 1) return 3;
            //函数调用自身,叫做递归调用
            return F(n - 1) + F(n - 2); 
        }
        static void Main(string[] args)
        {
            int res = F(10);
            Console.WriteLine(res);
            Console.ReadKey();
        }
    }
}

中断模式下的调试

中断模式指我们可以暂停程序的执行,然后查看程序中的状态,也可以让程序继续执行。

  1. 添加断点
    在这里插入图片描述
    在“调试-窗口-断点”可以查看当前项目中添加了的所有断点
  2. 监视变量的内容
    在这里插入图片描述
  3. 单步执行代码

逐语句和逐过程,两者都是一条一条语句执行,区别在于逐过程遇到函数时,不会进入函数内部,而把函数当成一条语句去执行。
在这里插入图片描述

错误处理(异常处理)

  • 处理异常的语法结构如下(包含三个关键字try catch finally)
	try
	{
	}
	catch ( <exceptionType > e)
	{
	}
	finally
	{ 
	}

其中catch块可以有0或者多个,finally可有可无。但是如果没有catch块,必须要有finally块;没有finally块,必须要有catch块,两者必须一者存在,也可以同时存在。

块介绍:

  • try块包含了可能出现异常的代码(一条或者多条语句)
  • catch块用来捕捉异常,当代码发生异常,那么异常的类型和catch块中的类型一样的时候,就会执行该catch块;如果catch块的参数不写,表示发生任何异常都可以执行
  • finally块包含了始终会执行的代码,不管有没有异常产生都会执行
namespace _013_错误处理_异常处理_
{
    class Program
    {
        static void Main(string[] args)
        {
            try //把可能出现异常的语句放在try里面
            {
                int[] myArray = { 1, 2, 3 };
                int myEle = myArray[4];
            }
            catch (IndexOutOfRangeException e ) //捕捉的异常类型,若不符合条件程序也会异常中断
            {
                Console.WriteLine("发生了异常:IndexOutOfRangeException");
                Console.WriteLine("访问数组的时候下标越界");
            }
            finally //错误处理中始终会执行的代码
            {
                Console.WriteLine("这里是finally里执行的代码");
            }

            Console.WriteLine("test");
            Console.ReadKey();
        }
    }
}

异常处理案例

namespace _014_错误处理案例
{
    class Program
    {
        //让用户输入两个整数,然后输出和
        static void Main(string[] args)
        {
            int num1, num2 = 0;
            Console.WriteLine("请输入第一个整数");
            while (true)
            {
                try
                {
                    num1 = Convert.ToInt32(Console.ReadLine()); //如果有一行代码发生异常,那么try中剩余的代码就不会执行了
                    break; //输入正确跳出循环
                }
                catch (Exception)
                {
                    Console.WriteLine("请输入一个正确的整数");
                }
            }

            Console.WriteLine("请输入第二个整数");
            while (true)
            {
                try
                {
                    num2 = Convert.ToInt32(Console.ReadLine()); //如果有一行代码发生异常,那么try中剩余的代码就不会执行了
                    break; //输入正确跳出循环
                }
                catch (Exception)
                {
                    Console.WriteLine("请输入一个正确的整数");
                }
            }

            Console.WriteLine("和为:" + (num1 + num2));
            Console.ReadKey();
        }
    }
}

匿名类型

创建变量的时候可以不指定类型,可以使用var声明一个匿名类型。
注意:使用var声明的匿名类型,当初始化的时候,这个变量的类型就被确定下来,不可以改变类型

var i=100;
i=1000; //同类型修改
//i="abc" //不能修改类型

堆和栈—程序运行时的内存区域

我们把内存分为堆空间栈空间

栈空间比较小,但是读取速度快。堆空间比较大,但是读取速度慢

栈(先进后出)

  • 数据只能从栈的顶端插入和删除
  • 把数据放入栈顶称为入栈(push)
  • 从栈顶删除数据称为出栈(pop)
    在这里插入图片描述

堆是一块内存区域,与栈不同,堆里的内存能够以任意顺序存入和移除
在这里插入图片描述

GC-Garbage Collector垃圾回收器

CLR的GC就是内存管理机制,我们写程序不需要关心内存的使用,因为这些都是CLR帮我们做了。
在这里插入图片描述

值类型和引用类型

  • 值类型:包括整数、bool、struct、char、小数。值类型只需要一段单独的内存,用于存储实际的数据(单独定义的时候放在栈中)
  • 引用类:string、数组、自定义的类、内置的类。引用类型需要两段内存
    • 第一段存储实际的数据,它总是位于堆中;
    • 第二段是一个引用(位于栈中),指向数据在堆中存放的位置
      在这里插入图片描述
      PS:如果数组是一个值类型的数组,那么数组中直接存储值;如果是一个引用类型的数组(数组中存储的是引用类型),那么数组中存储的是引用(内存地址),引用再分别指向相应的内存。
Vector3[]vArray = new Vector3[]{new Vector3(),new Vector3(),new Vector3()};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值