基础加强01
- 面向对象编程:【Object – Oriented - Programming】简称"OOP";
- 面向对象三大特性:封装、继承、多态。
- 类也是一种数据类型。
-
快捷键2
ctrl+k+c 表示 注释;
ctrl+alt+F10 表示 生成函数;
ctrl+k+u 表示 取消注释
ctrl+k+D 和ctrl + k+f皆表示 调整为规范格式;
- "即时编译" 的意思是程序运行到哪,就编译到哪!
-
传递多参数:定义一个数据数组,然后在类型前面加上params,就可以实现多参数传递.
private static int GetMaxNumbers(params int[] pms)
{
int max = pms[0];
for (int i = 1; i < pms.Length; i++)
{
if (pms[i] > max)
{
max = pms[i];
}
}
return max;
}
调用:
int max = GetMaxNumbers(10, 45, 13, 45, 75, 56);
Console.WriteLine("最大值为{0}", max);
***&* 该程序意在决出最大值。
7. 如何判断一个数是否为质数:
【这个数】%【除了1和它本身之外的任何整数】!=0
就说明这个数为质数!
8. Math.Round(double 变量名, 2);
该句话的意思是:"使用四舍五入的方法,保留后面的两位小数";
9. IndexOf的使用小例:
有这么一个字符串,如下:
string ziFu = "患者:"我老是咳嗽",医生:"你说啥?",患者:"痰多有咳嗽,",医生:"哦,你咳嗽呀。",患者:"恩恩,你帮我看看",医生:"一有病呀,我们正在演戏。"";
在这里注意,字符串数组可以看作是Char类型的数组,但本质不是char数组。可以当做数组来用!
int count = 0;
int index = 0;
//在邮箱账号中,为了保证用户输入了1个@,就应该让"变量名.LastIndexOf("@")==变量名.IndexOf("@")"即,就是保证最后出现的索引和第一次出现的索引相同。
while ((index = ziFu.IndexOf("咳嗽", index)) != -1)
{
count++;
index = index + "咳嗽".Length;
Console.WriteLine("第{0}次出现的索引为:{1}", count, index);
}
Console.WriteLine("咳嗽出现的总次数为{0}",count);
***&* 上面的ziFu.IndexOf("咳嗽", index)应该理解为:
变量名.IndexOf(string 参数1,int 参数2)
参数1为要搜索的字符串,参数2表示从索引为参数1的位置上开始搜索。且,返回值为int类型,代表着,从参数1开始,第一次出现的为值索引值!
10. 查询字符串中各个字符出现的次数:
#region查询字符出现了几次
//===================================================
Dictionary<char, int> dict = new Dictionary<char, int>();
for (int i = 0; i < ziFu.Length; i++)
{
***&* 字符串可以当做字符数组来用!
***&* ContainsKey(a)方法,使用来判断创建的字典集合中是否包含字符a;返回值为bool类型。
if (!dict.ContainsKey(ziFu[i]))
{
***&* 调用字典集的Add方法,即其定义为:public void Add(TKey key, TValue value);
// key代表'键',在这里代表字符,value 代表'值'在这里代表字符出现的次数!
dict.Add(ziFu[i], 1);
}
else
{
***&* public TValue this[TKey key] { get; set; }
//让字符出现的次数加1;
dict[ziFu[i]]++;
}
}
***&* KeyValuePair<> 称为键值对。
//便利整个字典集,输出键值对的值!
foreach (KeyValuePair<char, int> i in dict)
{
Console.WriteLine("字符'{0}',出现了{1}次", i.Key, i.Value);
}
//==================================================
#endregion
11. 去掉字符串中的空格:
string msg = " hello world 你好世界 ";
//因为Trim()有返回值,所以,必需接收返回值,否则,msg的是不会改变的!
//msg.Trim();//去掉两边的空格
msg = msg.Trim();
//以空字符为分隔符,然后移除空的字符串
string[] word = msg.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
// RemoveEmptyEntries的意思就是:"返回值不包括含有空字符串的数组元素!"通过 StringSplitOptions 赛选留下自己需要的东西。
msg = string.Join(" ", word);
// Join的意思是:"串联字符串中的所有元素,通过指定的字符来连接"
Console.WriteLine("=========" + msg + "==========");
12. ToLower()方法即"将字符串转化为小写形式"
例如:
Console.WriteLine(Name.ToLower())
将name全部转化为小写字符串。
13. 将字符串转化为字符数组:
char[] array = num.ToCharArray();
转化为字符数组是有必要的,如下:
String name="liqianlong";
//如果我要改变字符串中的一些字符,下面的做法是不正确的!
Name[2]='8';
但要是想取它的值,下面是合法的:
Char n=Name[2];
所以将字符串转化为字符数组是很有必要的!
14 . 通过索引来访问对象的字段值
在Main()方法里
ItcastClass ic = new ItcastClass();
for (int i = 0; i < ic.Count; i++)
{
Console.WriteLine(ic[i]);
}
在ItcastClass中:
public class ItcastClass
{
public int Count
{
get
{
return _name.Length;
}
}
private string[] _name = {"杨中科","蒋坤","赵鹏"};
//创建索引器
//索引器的特点为:
// 1. 没有具体的名称;
// 2. 就是特殊的属性;
// 3. 一个类中只有一个索引;
// 4. 索引可以重载;通过参数的不同,就可以将索引重载;
// 5.
public string this[int index]
{
get
{
return _name[index];
}
set
{
_name[index] = value;
}
}
}
15. 触发事件:
private void button1_Click(object sender, EventArgs e)
中的sender表示:"触发事件的控件";
EventArgs e 表示:"事件的相关信息";
比如说:sender 代表控件这个对象
E 代表鼠标的操作等。
16. Base Class ->基类、 Parent Class ->父类
Derived Class ->派生类 Child Class->子类
基类和父类的都属于父类
派生类和子类都属于子类
17. 调换两个数字【扩展】:
Int a=10,b=15;
A=a^b;
B=a^b;
A=a^b;
Console.Writeline(a);
Console.writeline(b);
18. 继承的特性:
1.单根性 – 一个类只有一个父类;
2. 传递性 – 属性或方法相传
19. /*03----2013.06.01---基础加强--静态成员,静态类,静态构造函数,多态,里氏转换,抽象类实现多态,值类型余引用类型,值类型与引用类型作为参数传递时的问题,引用传递ref*/
- 面向对象:封装、继承、多态
-
封装:
1. 能够实现对字段友好的控制,从而增加安全性能。比如:
- 加入你想传入多个参数,就可以将多个参数封装成一个对象,然后将这个对象传过去。就可以了
- 实现代码的重用也是封装。
- 继承的两个好处:【代表类与类之间的关系】
-
代码重用;
继承具有单根性、传递性。
-
实现多态;【增加程序的可扩展性、灵活性。】
2.1 如何实现多态:【通过虚方法实现多态】
在父类方法前面加上"Virtual"【将这个方法标记为虚方法】,然后继承该类的子类。如果你希望重写这个方法的话,那么就在这个方法前面写上Override。
注意:父类中的虚方法,子类可以重写也可以不重写。
所谓的重写就是:"子类中的方法名、返回值类型、参数类型及个数一定要相同。否则不能称作为重写。"
问题:
如果基类和子类都有一个同样的方法SayHi(),那么基类中的SayHi()方法前面必须加上Virtual,然后子类中的SayHi()前面必须加上Override,才算是将父类 中的方法进行覆盖。否则要是两个方法相同的话,就会有问题。然后要是在子类前面用new关键字,这样的作用就是将父类的方法隐藏。这样的话就不叫方法重写了。
通过上面的方法就可以达到隐藏父类中的方法,然后可以通过base.M1(),这样是调用父类的M1()方法。
里氏替换原则:
如果你需要一个父类类型时,给了你一个子类类型,也是可以的。
例如;
Person per=new Student();
注意:
当一个子类继承父类以后,该子类中的所有构造函数默认情况下,在自己调用之前都会先调用父类中的无参数的构造函数,如果此时父类中没有无参数的构造函数,则提示报错。
然后要想不让调用无参的构造函数,而去调用有参的构造函数,就这样做:
这里的【:base】的意思是调用父类中的构造函数。然后需要注意的是,构造函数是不能继承的。只能被调用。
这里应该注意的一点就是:"子类中想调用父类中的构造函数,那么子类中传入的参数的位置是可以随便排序的。"
同理,可以用this调用本类中的其他构造函数,这样减小代码的重复。
-
访问修饰符;
Public 任何地方都能访问到。
Internal 可以在整个程序集中访问。
Protected 在本类与子类中可以访问。
Private 在本类中可以访问到。
注意:在成员变量中,如果不写访问修饰符,那么就默认是private。而类的访问修饰符不写是internal.并且类的访问修饰符只能是public 和 internal。这只是对程序员进行限制的,而对于微软内部是可以使用其他修饰符的。
-
用this和base调用成员的时候,应注意:
在子类中,使用this和base调用其成员变量时,实例化一个对象,然后先是去父类中调用父类的构造函数,然后在去调用自己的构造函数。调用父类的构造函数主要是对该对象的一些成员变量进行初始化,要是在自己的构造函数中再次初始化,这样只会对同一变量的值进行覆盖。
对于子类中继承下来的成员变量,不管是使用base还是this,都指的是同一空里的东西。而在这里注意:而对于继承下来的方法,如果子类重写了该方法,那么用base调用的是父类中的方法。如果不重写,就代表一个东西。
25.
Person p1=new Student();
Student s1=(Student)p1;
这样是可以的。
Person p2=new Person();
Student S2=(Student)p2;
这样是不可以的。
- 方法的访问修饰符需要和方法的参数类型和返回值类型的访问修饰符一致。
- 方法的重写:父类中有virtual [实现虚方法或者虚属性]子类中有 override
-
多态:通过虚方法实现多态;
假如一个对象person[]数组,里面包含我们所知道的人,入"中国人"、"美国人"等。然后让子类调用各自的SayHello()方法,然后就可以通过方法的重写或者方法的重载,来实现多态。
-
方法签名:
一般指方法的【名称】+方法的【参数列表】,不包含方法的返回值;类型。
30. 隐藏在父类中继承下来的方法或这属性:
用new 关键字:
如果子类中的方法与父类中的方法一样,但又不想重写该方法,那么就可以用new关键字解决。通过this调用这个方法,一定调用的是子类中的方法。但是如果用base调用调用这个方法,则调用的是父类中原来的方法。
通过简单的例子来理解隐藏和重写的含义:
假如有这么两个类:Persons和 Son
Public static void Main(string[] args)
{
Persons m1=new Son()
Son.SayHello();
}
然后如果Son重写了Persons类的方法SayHello(),通过上面的调用,调用的应该是Son类中的方法。如果是隐藏父类中的方法,那么通过上的方法调用,调用的是父类中的方法。
-
静态成员是属于类的,而不属于任何对象。也就是说没对象也能调用这个方法。
因此在访问静态成员的时候,不能通过对象来访问,(对象.属性名),只能通过"类名.属性名"来直接访问。
注意:在程序的任何地方访问静态成员时,都访问的是同一内存。
在实例类中如果你操作的方法与对象无关的,那么这个方法就可以设置为静态方法。否则不能设置为静态方法。
在静态成员中存在的问题:静态成员为了能够在程序中的任何地方都能访问,它将在程序关闭之前不会被释放,直到程序关闭之后才被释放。而实例成员会在用完之后就会被回收。
-
当一个类的构造函数变为私有的,你在其他类中无法将其实例化,我们可以利用该类的私有方法对对象进行实例化。
当实例化对象的时候,可以通过调用该方法对该类进行实例化。
-
静态类和实例类的区别:
- 静态类不需要实例化,就可以通过类名直接访问。而实例类就不行。
- 在静态类中的所有成员都必须是静态的,
- 不是所有的静态成员都是在静态的类中。
-
静态成员的一种使用情况:【如果构造函数为私有的,那么就意味着不能够实例化】
- 类属于引用类型,结构属于值类型。
- 一个类中,会默认有一个自己的构造函数,当这个【实例类】中有个静态的成员变量并且给其赋值,然后就会自动生成一个静态的构造函数。如果没有给其赋值,那么他是不会生成静态的构造函数的。并且在静态的构造函数里面,会将静态成员初始化为你附的值。
-
在调用一个对象时,会首先去调用其构造函数,对于静态的构造函数是没法手动去调用的,所以是不能为静态构造函数添加访问修饰符的。其默认的访问修饰符是private.
注意:既然访问修饰符不是你来去手动调用的,它是没有参数的。
类中的静态成员,在第一次使用静态类的时候进行初始化。
-
类在加载的时候会先去加载这些字段,然后执行代码。如果有静态字段,那么就会去找静态的构造函数,如果你没写,当你在定义的时候给静态字段赋值了,那么他就会自动给你创建静态的构造函数。否则他是不会去创建的。
静态构造函数只会被执行一次。是在第一次使用类或者是静态成员的时候执行。
然后如果一个类中即有实例类构造函数,还有静态类构造函数,他会去先执行静态的构造函数。然后再去执行实例类构造函数。这个具体的说明了之前的那句话:【静态构造函数会在执行类或者是静态成员的时候执行】
-
多态:【多种表现形态】
多态的作用:把不同的子类对象都当做父类来看待,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。、
实现多态的方式:
- Virtual ,子类重写父类方法。
- Abstract 子类重写父类的方法。
- 接口,实现接口的类,将接口中的方法实现。
-
验证:
is-a:可以验证继承关系中是否合理。
If(obj is 类型A){} //obj是父类类型对象,"类型A"是子类类型。
Can-do:验证实现接口是否合理。
-
类型转换的另外一种方式:【使用关键字as】
Person p=new Student();
Student s=p as Student;
如果转换成功,就返回Null,否则就返回这个对象。然后继续运行程序。
注意:它与强制类型转换的区别是:
强制类型转换时,当转换不成功就会报错。而使用这种方法是不会报错的。在这里建议使用这种方式。代码在执行过程中,使用这种方式效率比较高。
-
抽象类 abstract: 它的作用就是用来实现多态的。
- 抽象类是不能被实例化的。就是被继承的。
- 抽象类中可以有实例成员,也可以有抽象成员。
-
抽象成员不能有任何实现。
范例:public abstract void SayHi();
- 抽象成员必需包含在抽象类中。
- 抽象成员子类继承以后必需要用override"重写"除非子类也是抽象的。
既然抽象与虚方法都能实现多态,那么如何去选择使用他们的呢?
- 看是否需要被实例化。因为抽象类是不能被实例化的。
- 方法需不需要被实现。因为抽象类中的方法是不能被实现的。
注意:在开发项目中需要强调的一点是,能用抽象的,尽量用抽象的。
-
@@***************** 08封装计算方法实现面向对象计算器 需要练习
43 . 引用传递:
//传的是栈本身的地址。。
前面加上ref时,如上例,通过M1函数传递m时,是将m所在的栈的地址给了n,即m和n就指向了同一个东西。
Out和ref 作用是一样的,但是你将数据传出去,而传不出来。即out只能传出去,不能传进来。
44. 方法重载【overload】和方法重写【override】
区别一:
Overload 【重载】是在一个类中实现的方法的重载。
Override【重写】是子类与父类
区别二:
方法重载是在编译期间就已经能够确定调用的是那个方法了。
而方法重写是在程序运行时才能够确定调用的是那个方法了。
方法的重载:
方法重载和返回值类型没有关系!只与参数的类型有关!参数的个数的不同也可以构成重载!
-
什么是接口:
接口是以一种规范,协议(*),约定好遵守某种规范就可以写通用的代码。
例如:对于U盘来说:
接口是?
USB插口的标准就是一个接口
在那个类中体现?
然后U盘实现了这样的接口。相当于他有了这样的功能。直白一点就是U盘符合USB插口的标准。
在哪里实现多态?
我的U盘可以用在各种各样的电脑上,或者支持USB接口的机器上。
定义了一组具有各种功能的方法。(只是一种能力,没有具体的实现,像抽象方法一样,"光说不做"【由具体的对象来实现。】和抽象类一样,里面的方法没有任何实现。)
即例子:
抽象类中的方法:
Public void SayHello(); //里面不作任何处理。
44. 接口存在的意义:实现多态。
多态的意义:程序可扩展性。最终-》让程序变的更灵活,节省成本,提高效率。
45. 接口如何写?
定义接口用关键字interface 然后接口的名称一般以大写I开头。这样以后看见以I口头的关键词就知道是接口。最后都是以able结尾。
46. 接口里面的成员:
可以包含"方法"、"属性"、"索引器"、"事件"。但归根结底还是以方法的形式表现的。严格来说:只能包含方法。
注意:接口里面的所有成员都不能有显式访问修饰符。为了保证接口能够被实现,人家默认是public.方法里面不能包含任何实现。
可以有参数。
接口不能实例化。
接口中的成员,子类必须实现。
47. 实现多态:通过虚方法、抽象类或者抽象方法、还有就是接口。
48. 既然虚方法和抽象类都能多态,为什么还要引入接口?
1. 对于类来说,继承具有单根性。那么有时候我想继承多个类咋办?那么就用接口。一个类只能继承一个父类,但可以实现多个接口。
接口解决了单继承的缺点。
2. 接口解决了不同类型之间的多台问题,不如说鱼和船不是同一类型,但是能在水里"游泳"。只是方式不一样,要对"游泳 "实现多态,就只能考虑接口。
这里注意:
如果即实现多个接口又要继承父类,那么,就必需将父类写在最前面,接口放在后面。
当父类和接口中有同名的方法,那么,就是显式接口。
49. 简单的实现接口程序:
namespace_002
{
classProgram
{
staticvoidMain(string[] args)
{
IConHomeWorkconHomeWork=newStudent();
conHomeWork.ConWork();
Console.ReadKey();
}
}
publicinterfaceIConHomeWork
{
voidConWork();
}
publicclassStudent:Person,IConHomeWork
{
publicvoidConWork()
{
Console.WriteLine("学生收作业。");
}
}
publicclassTearcher:Person,IConHomeWork
{
publicvoidConWork()
{
Console.WriteLine("老师收作业.");
}
}
publicclassSchoolMaster:Person
{
}
}
50. 对于一个普通的类来说,如果实现了一个接口,那么就要实现接口中的每一个成员,但对于一个抽象的类来说,可以不去实现它,让它的子类去实现这个方法。
51. 显示实现接口:【就是为了解决方法名重名的问题】
【当方法命名重名的时候,才会用到显示实现接口。】
比如说:有一个类实现了两个接口,恰巧两个接口中都有一个SayHello()方法。然后这个类实现接口中的方法后,这个类中就有两个SayHello()方法,然后就不好确定哪个方法是实现那个接口的。
注意:类或者接口中前面如果没有访问修饰符时,那么就默认为internal。然后当你写一个类继承或者实现某个接口的时候,就会出现提示信息,然后提示信息对应的前面会有一个小信封图标。如下图:
对于是internal类,前面的图标为:
对于是internal接口,前面的图标为:
52. 小错误:
由于子类中访问修饰符大于父类中的访问修饰符,所以会报错。
53. 接口总结:
·接口是一种规范。为了多态。
·接口不能被实例化。
·接口中的成员不能加"访问修饰符",接口中的成员访问修饰符为public,不能修改。(默认为public)
·接口中的成员不能有任何实现("光说不做",只是定义了一组为实现的成员。)
·接口中只能有方法、属性、索引器、事件,不能有字段。
·接口与接口之间可以继承、并且可以多继承。
·实现接口的子类必须实现该接口的全部成员。
·一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承父类A,并实现了接口IA,那么语法上A必须写在IA的前面。class MyClass:A,IA{},因为类是单继承的。
·当一个抽象类实现接口的时候,如果不想把接口中的成员实现,可以把该成员实现为abstract。(抽象类也能实现接口,用abstrac标记。)
·"显示实现接口",只能通过接口变量来调用(因为显示实现接口后成员为private)。
54. 使用接口的建议:
·面向对象编程,使用抽象(父类、抽象类、接口)不使用具体实现。
·"向上转型"
·在编程时:
·接口-》抽象类-》父类-》具体类(在定义方法参数、返回值、声明变量的时候能用抽象就不要用具体。)
·能使用接口就不用抽象类,能使用抽象类就不用类,能用父类就不用子类。
·避免定义"体积庞大的接口"、"多功能接口",会造成"接口污染"。只把相关联的一组成员定义到一个接口中(尽量在接口中少定义成员)。单一职责原则。
·定义多个职责单一的接口(小接口)(组合使用)。(印刷术与活字印刷术)
55. 在C#中,switch-case中的break不能缺少,但在java中是可以缺少的。
56. 方法的总结:
·对于一个方法本身来说,它不可以自己调用自己。因为这样会导致内存溢出。程序会不停的调用自身的方法,没有结束语句。
·对于一个有返回值的方法来说,我可以定义一个变量来接收,也可以不去接收它的返回值。程序运行的时候是不会报错的。
57. 数据类型转换:
1. 隐式类型转换:
·源数据类型小于目标类型;
·一般用于数值类型。
注意:在int 、char 、byte、short、long之间也是可以进行转换的。可以这样理解,其实char类型本质是数值类型的。
58. 通过sizeof(类型)可以求出类型所占用的字节数。
59. 获取一个成员的类型:
基本语法为:
·成员名.GetType().ToString(); //得到成员的类型。
·成员名.GetType().BaseType().ToString(); //得到成员的父类类型。
·成员名.GetType().BaseType().BaseType().ToString() //得到成员的父类的父类类型。
如下图所示:
注意:使用这种方法的弊端是:如果已经到了最终的父类后,还要找它的父类,那么程序就会报错。
所以,一般情况下我们不建议使用这种方式。我们可以使用as来判断成员的类型是什么。。
as 的使用:
比如说有:一个Person类、Student类、BestStudent类,然后student继承自Person类、BestStudent继承自Student类。
如下图所示:
对于上面的代码,如果p是BestStudent那么就会 对于这个问题,百度as的作用。没听懂。
60. 异常处理1:
·捕获异常机制:
try
{
//可能发生异常的代码。
}
catch
{
//发生异常的最后的处理。
throw;//处理完成异常之后,要对上报异常。用throw就是继续处理异常。
}
finally
{
//无论发不发生异常,都一定要执行的代码。
}
注意:一个try块可以跟一个或者多个或0个catch块,后面跟finally也行不跟也行。finally只能有一个。
·当你让屏幕显示异常,那么就如下这样写:
try{}
catch(Exception ex)
{
Console.WriteLine(ex.Message) //显示异常的信息。
Console.WriteLine(ex.Source); //显示异常发生在那个命名空间里面
Console.WriteLine(ex.StackTrace); //显示异常的跟踪信息,具体显示异常发生在哪一行的代码中。
}
finally{}
这样就可以将出现的异常显示在屏幕中。
·catch中的几种写:
try{}
//当出现空指针异常的时候,就会走这里。
catch(NullReferenceException ex){}
//当出现除数为0的时候,就会走这里。
catch(DivideByZeroException ex){}
//当出现参数异常的时候,就会走这里。
catch(ArgumentException ex){}
//当出现其他异常的时候,就会走这边。
catch(Exception ex){};
注意:不要将catch(Exception ex){};放在最前面,因为这句话是接收所有异常的,所以当放在最前的,出现异常时就会走这句话,其他catch是不会走的。
61. 异常2:
要是想让代码不论任何情况下,代码都能够执行。就要用finally语句。有的时候,不用finally,而且还想让catch后面的语句执行时,有些情况是不允许的。例如:
·catch中如果有return时且出现异常,那么就不会执行后面的代码了,但是当你将代码放在finally中,就可以让这段代码执行。
·当catch有无法捕获到的异常时,程序崩溃,但在程序崩溃前会执行finally中的代码,而finally块后的代码则由于程序崩溃了无法执行。
62. 异常3:【手动引发异常】
例如:
staticvoidMain(string[] args)
{
while (true)
{
Console.WriteLine("请输入一个人的年龄?");
stringname=Console.ReadLine();
try
{
if (name=="黄林")
{
thrownewException("年龄不合法!");
}
else
{
Console.WriteLine("合法姓别!");
}
}
catch (Exceptionex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
63. 异常4:
对于上面的例子,M2调用M1,然后又在Main方法中调用, 然后通过throw,将当前的异常继续向上抛出。。
注意:M2的处理代码如下:
try
{
M1();
}
catch
{
Console.WriteLine("M1放噶中发生异常了。");
throw; //继续向上抛出异常。
}
注意:一般不要用抛出异常的方式来,而应该给用户输入一句话来提示用户。
64.加强:
privatestaticintM1()
{
intresult=100;
try
{
result++;
returnresult;
}
catch
{
result++;
returnresult;
}
finally
{
Console.WriteLine("==================");
result++;
}
}
注意:根据提示可知,针对上段代码,先走的是try里面的,然后已经将return执行完成之后,然后再执行finally里面的代码。
即这段代码的本质是如下图:
注意看图片上面的返回值,他返回的是而不是result.之所以会自动添加变量,是因为编译器在返回值的时候,会给你自动声明返回值变量,然后当程序员return 的时候,其实返回的时候并不是你指定的变量,而是编辑器自动生成的变量。
还有一种情况就是:对于上面的int result是值类型,而对于引用类型来说,结果就不一样了。
例如:
privatestaticintM1()
{
Person per=new Person();
per.Age=100;
try
{
per.Age++;
【中间出现异常代码省略】
returnp;
}
catch
{
per.Age ++;
returnp;
}
finally
{
Console.WriteLine("==================");
per.Age++;
}
}
通过反编译来看:
然后在调用的时候,结果是:
·不出现异常是:102;
·出现异常是:103;
65. 参数:
可变参数:
在定义的时候,用数组来表示。具体形式表示为:
void 方法名(params int[] args);
注意的事项:
1. 如果方法有多个参数,可变参数必须作为最后一个参数。
2. 可变参数可以传递参数也可以不传递参数。如果不传递参数,则数组args[]是一个长度为0的数组,而不是null;
3. 可变参数可以直接传递一个参数。
4. 也可以传递一个null过去。所以到你想用数组args[]时,就必需判空。
66. out – ref
方法的重载中不支持out和ref的,也就是说,只有out 和 ref 的区别是构成不了重载的。
例如:
void SayHello(ref string name)
{
}
voi SayHello(out string name)
{
}
这样子是构成不了重载的,程序是不支持的。因为它们大体上的作用是一样的。
67. 比较两个对象是否为同一对象:
方法一:
使用object.ReferenceEquals(对象一,对象二); 注意返回值类型为bool类型。
方法二:
对象名.Equals(对象二); 有返回值,返回值类型为bool 类型。
方法三:
使用等值语句:
例如:if(p1==p2)
{
Console.WriteLine(true);
}else
{
Console.WriteLine(false);
}
整套代码如下:
staticvoidMain(string[] args)
{
Personp1=newPerson();
p1.Name="liqianlong";
p1.Age=21;
p1.Sender='男';
Personp2=newPerson();
p2.Name="liqianlong";
p2.Age=21;
p2.Sender='男';
Personp3=p1;
//比较两个对象是否为同一对象。
Console.WriteLine(object.ReferenceEquals(p1,p2)); //False
Console.WriteLine(object.ReferenceEquals(p1,p3)); //true
Console.WriteLine(p1.Equals(p2)); //False
Console.WriteLine(p1.Equals(p3)); //true
if (p1==p3) //true
{
Console.WriteLine(true);
}
else
{
Console.WriteLine(false);
}
Console.ReadKey();
}
这里要注意的一点是:字符串.Equals()和对象.Equals()是有区别的,区别如下:
- string的Equals()方法判断的是字符串的内容否相同(重写了Object中的Equals()方法。),
- Object的Equals()方法判断的是对象的堆地址是否此相同。如果相同说明是同一个对象,如果不相同,说明不是同一个对象。
- 而对于==来说,它的内部调用的是EqualsHelper()方法,和Equals()一样。
- 一般情况下,会使用,Object.ReferenceEquals()来比较两个对象是都为一个对象。
-