C#方法的定义、调用与调试

本节内容

1.方法的由来;

2.方法的定义与调用;

3.构造器(一种特殊的方法);

4.方法的重载(Override);

5.如何对方法进行debug;

6.方法的调用与栈*

*推荐书目:CLR via C#和C# in Depth, 3rd Edition

1.方法的由来

①方法(method)的前身是C/C++语言的函数(function)

方法是面向对象范畴的概念,在非面向对象语言中仍然称为函数。

可以使用C/C++语言做对比。

*当函数以成员的身份出现时我们就叫它方法(始于C++)

②永远都是类(或结构体)的成员

C#语言中函数不可能独立于类(或结构体之外)。

只有作为类(或结构体)的成员时才被称为方法。

而在C++中是可以的,称为“全局函数”。

③是类(结构体)最基本的成员之一

最基本的成员只有两个——字段与方法(成员变量与成员方法),本质还是数据+算法

方法表示类(或结构体)“能做什么事情”。

④为什么需要方法和函数

目的1:隐藏复杂的结构;

目的2:复用(resue,重用);

C++中#include<iostream>

#include "Student.h"//如果是标准类库的话用尖括号,如果是自己定义的则用引号。

2.方法的声明与调用

①声明方法的语法详解

参见C#语言文档(声明/定义不分家);

Parameter全称为”formal parameter”;

形式上的参数,简称”形参”:形式参数参与构成算法的算法逻辑(即在算法中作为变量参与计算);

Parameter是一种变量:形式参数变量;

②方法的命名规范:

大小写规范:pascal法则(每个单词首字母大写);

需要以动词或者动词短语作为名字;

③重温静态(static)方法和实例方法:静态方法是隶属于类的,类的对象不能调用;实例方法(非静态方法)隶属于类的对象,对象可以调用。

④调用方法:方法名后面的圆括号不能省略。

声明函数时”()”里面的是形参;调用函数时”()”里面是实参。

Argument中文C#文档的官方译法为”实际参数”简称”实参”可理解为调用方法时的真实条件。

调用方法时的Argument列表要与定义方法时的parameter列表相匹配,C#是强类型语言,argument是值,parameter是变量,值与变量(个数与数据类型)一定要匹配不然会报错。

3.构造器

构造器(constructor)是类型的成员之一;

狭义的构造器是指“实例构造器”(instance constractor);

如何调用构造器;

声明构造器;

构造器的内存原理;

构造函数的作用为构造类的实例(对象)并初始化。

①当调用默认构造函数时:

如语句:Student stu=new Student();①

Class Student

{

Public int ID;

Public string Name;

}

*由于①语句是在main函数了的,stu引用变量为局部变量,先在栈中分配4个字节的空闲内存;*new操作符创建了Student类的一个实例,通过调用Student();默认构造函数,在堆内存中开辟一块空内存大小为该实例所包含的数据之和的空间存放实例,(int ID;4个字节,string name4个字节)所以分配8个字节的内存空间,由于默认构造函数默认赋值为0,故该块内存里的数据初始值为0;*最后将该内存的首地址转化为2进制数,复制到引用变量stu的栈内存当中。

②当调用有参构造函数时:

第一步和第三步操作与调用默认构造函数时相同;不同之处在于第二步:

&当参数为数值类型时:首先在堆内存上这8字节的实例内存空间里,int类型占4个字节,传进来的实参值为1(假设)则把1(都要转化为2进制)转化为2进制存在该内存里;

&当参数为引用类型时(记住凡是类类型都是引用类型):剩下的4个字节是string类型的存储空间,(假设传进来的实参为”it’OK”)

由于string是类类型的,而类类型是引用类型,所以,这4个字节里面存放的只是引用变量,还要再去堆内存中找一块位置存放实参”it’OK”。找到内存空间后把”it’sOK”这个字符串数据转化为2进制数据存入其中。(最后该内存首地址转化为2进制存在string类型栈内存中与上面一致)

4.方法的重载(Overload)

①调用重载方法的示例;

②声明带有重载的方法

方法签名(method signature)由方法的名称、类型形参(讲泛型的时候会遇到)的个数和它的每一个形参(按从左到右的顺序)的类型和种类(值、引用(ref)或输出(out))组成。方法签名不包含返回值。(主要看方法名和参数列表)

实例构造函数签名由它的每一个形参(按从左到右的顺序)的类型和种类(值、引用或输出)组成;

重载决策(到底调用哪一个重载):用于在给定了参数列表和一组候选函数成员的情况下、选择一个最佳函数成员来实施调用。

5.如何对方法进行debug(核心技能)

设置断点(breakpoint);

观察方法调用时的call stack;

Step-in,Step-over,Step-out;

观察局部变量的值与变化。(可以通过局部变量窗口观察,还可以把鼠标放在变量上观察,也可以把鼠标放在变量上出现的小窗口固定住来观察)

调用栈(call stack)和内存栈的关系:比如递归,不断的调用自己,直到有一个停止的值,若没有,则调用栈就会越来越深直到把内存栈占爆了,这时候就stackOverflow(栈溢出)

Step-in(F11)(逐语句):进入方法当中,最细腻的debug手段

Step-over(F10)(逐过程):不走进方法直接跳过去,比较粗犷一些的debug手段

一般为两者结合使用,先用F10快速的大范围的排查是哪个方法出现问题再使用F11走进方法一步一步debug。(怀疑有bug的地方就F11仔细的排查)

Step-out(跳出):作用为跳转到调用断点当前方法的类或方法里面(按顺序)例如3调用2,2调用1,则在1中设置断点,第一次按Shift F11跳到2处,再按一次跳转到3处。

*综合使用这三种方法会使我们具有强大的debug能力。

6.方法的调用与栈

①方法调用时栈内存的分配

对stack frame(一个方法在被调用过程中栈内的布局)的分析

*栈内存由高字节位向低字节位发展,发展到最低限度之后就会Overflow,

例如C#fun中的main函数输出语句依次调用三个方法,调用时,main方法先在栈内存里开辟一个内存空间叫stack frame(栈帧)。

方法的调用关系有调用和被调用,调用者叫做Caller,被调用者叫做Callee,那么问题来了比如main函数中调用其他函数: Console.WriteLine(c.GetConeVolume(100, 100));那么传进去的两个参数(100)哪个函数来管理呢,不同的语言规定不同,C#是按C++的标准,参数归主调者(caller)管,就是这里的main管。在栈内存中分别开辟参数相应类型大小的空间,先压哪个参数进去呢?不同的语言规定不同,C#遵从C++的规则先左后右,谁调用谁负责把参数压进栈里故该片内存也是由main管(也是main函数的stack frame),*即方法运行时都先在栈内开辟stack frame内存空间 再把方法内包含的数据都压进该片栈内存中。现在开始方法的调用,遵循谁调用谁压栈的原则,即使方法内无局部变量该方法也会有stack frame(存放方法返回值地址等),方法的返回值是存在cpu的寄存器(学汇编时有讲到)里面的。

*一个方法运行完的出结果返回了值之后,相应的stack frame(栈帧)就会被清空,相应的函数(方法)调用结束相关参数没有用了其在栈帧中的内存也会被清空。(可以联想汇编语言的压栈与出栈)

*这里也可以解释一下overflow,就是因为异常方法一直调用,一直

开辟栈帧,没有一层层的返回,相应参数只进栈不出栈,直至开辟空间到栈顶造成“栈溢出”这就是栈溢出的原理。

原文地址:https://www.cnblogs.com/AhuntSun-blog/p/11741789.html

时间: 2024-11-10 07:36:43

C#方法的定义、调用与调试的相关文章

方法的定义与调用以及static关键字的用法

一.方法的定义与使用(形参只有值传递)以下所有方法在主类中定义,并且在主方法中直接调用. 1.方法声明: 语法: [访问修饰符]  [static] 返回值类型  方法名(参数列表){ 要执行的代码 } //自定义方法 public static void print(){ System.out.println("hello"); } 当方法以void声明时,表示此方法无返回值.但是void方法仍然可以与return返回值配合使用;表示结束方法调用.java方法只有值传递 packag

方法的定义与调用

方法的定义: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,...){         方法体         return 返回值; } 修饰符: 固定写public static 返回值类型: 方法需要返回的数据的数据类型,如果返回没有返回值,那么返回值类型就是void 方法名: 自己取的名称 符合标识符命名规则和规范 方法中的参数:  用来接收传入方法中的数据 参数类型: 传入方法中的数据的数据类型 参数名:  自己取的名称 符合标识符命名规则和规范 方法体: 方法需

Scala 函数和方法的定义与使用

摘要: 函数是一组一起执行一个任务的语句. 您可以把代码划分到不同的函数中.如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的. Scala 有函数和方法,二者在语义上的区别很小.Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量.换句话来说在类中定义的函数即是方法. 我们可以在任何地方定义函数,甚至可以在函数内定义函数(内嵌函数).更重要的一点是 Scala 函数名可以有以下特殊字符:+, ++, ~, &,-, -- , \,

override(重写,覆盖) 1、方法名、参数、返回值相同。 2、子类方法不能缩小父类方法的访问权限。 3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。 4、存在于父类和子类之间。 5、方法被定义为final不能被重写。 overload(重载,过载) 1、参数类型、个数、顺序至少有一个不相同。 2、不能重载只有返回值不同的方法名。 3、存在于父类和子

override(重写,覆盖) 1.方法名.参数.返回值相同. 2.子类方法不能缩小父类方法的访问权限. 3.子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常). 4.存在于父类和子类之间. 5.方法被定义为final不能被重写. overload(重载,过载) 1.参数类型.个数.顺序至少有一个不相同.   2.不能重载只有返回值不同的方法名. 3.存在于父类和子类.同类中. 方法的重写(Overriding)和重载(Overloading)是Java多态性的不同表现. 重写(O

委托应用①——窗口之间方法/控件调用

转载请注明地址:http://www.cnblogs.com/havedream/p/4602974.html 最近重新拿起以前的书看,发现关于委托和事件一直没有很好的理解,或者说理解了也不知道到底这个东西有什么用,今天恰好碰到了一个窗口之间方法调用的问题(至于控件的调用其实是一样的,方式一样),问题描述如下: Form1中有一个刷新方法Refreash(),Form1用dialog方式打开Form2,我需要在更改完Form2后,也就是关闭Form2的时候刷新Form1,也就是调用Form1的R

22.java方法的定义

java中的方法:就相当于c语言中的函数:sun在开发java的时候,为提高其代码的重复利用率,引入了方法. 什么是方法? 方法就是一段代码片段,这个片段可以完成特定的功能,并且可以重复利用. 从入口程序那里开始调用,一掉就开始执行指定的方法. 定义方法的语法: [方法的修饰符列表] 方法的返回值类型 方法名{ java语句; } 注意:[]括号里面的可以有也可以没有,但是不在[]里面的一定的有. 1)[方法的修饰符列表]是可选项,现在暂时先写成:public static 2)方法的返回值类型

java中方法的定义

java中方法的定义: [修饰符]   [修饰符]   [返回值类型] 方法名字 [形参列表] //带[]的可以省略,返回值类型不能省略. 就拿main方法来说,public         static       void       main (String[] args){ 方法体: return 返回值://返回值要与返回值类型相匹配.没有返回值的话,返回值类型用void; } 方法的定义:为了完成某项功能,封装的一系列代码的集合: 方法的调用:同一个类中可以直接用方法名():调用:但

js调用.net后台事件,和后台调用前台等方法以及js调用服务器控件的方法

http://blog.csdn.net/deepwishly/article/details/6670942  ajaxPro.dll基础教程(前台调用后台方法,后台调用前台方法) 1. javaScript函数中执行C#代码中的函数: 方法一:间接触发后台代码 1.首先建立一个服务端控件按钮命名为btn1,双击进入后台将调用或处理的内容写入btn1_click中; 2.在前台写一个js函数,内容为document.getElementByIdx("btn1").click(); 3

第2.6章:方法的定义与使用

方法的基本概念 方法的主要功能是封装可以执行的一段代码,这样不仅可以进行重复调用,更可以方便的实现代码的维护,而本次使用的方法定义语法如下所示. public static 返回值类型 方法名称(参数类型 参数变量, ...) { 方法体(本方法要执行的若干操作) ; [return [返回值] ;] } 在本定义格式之中,发现方法有一个返回值类型,指的是这个方法返回结果,对于此类的类型可以有两种: 直接设置Java中的数据类型(基本数据类型.引用数据类型), 如果方法设置了返回值,那么必须使用