C# 一些知识点总结(一)_继承,多态,集合,关键字...
内存中的三大存储区域:栈、堆、静态存储区域
new 关键字:
new帮助我们做了三件事:
1)在内存中开辟一块空间(开辟内存空间)
2) 在开辟的空间中创建对象
3)调用对象的构造函数进行初始化对象
所以构造函数的修饰符必须为public
作用:
1)创建对象
2)隐藏基类那里继承过来的同名成员。隐藏的后果就是派生类调用不到基类的成员。
如要隐藏派生类中与基类相同名(指相同方法签名)的方法:A类继承了B类,A、B类中有相同方法签名的方法C( ),可以在基类中public C( ){} ,在派生类中public new C( ){}
return 关键字:
作用:
1)在方法中返回要返回的值。
2)立即结束本次方法。
this 关键字:
1)代表当前类的对象
2)在类中显式的调用本类的构造函数,用法是 :this
例:
1 public class Student 2 { 3 public Student(string name, int age, char gender, int chinese, int math, int english) 4 { 5 this.Name = name; 6 this.Age = age; 7 this.Gender = gender; 8 this.Chinese = chinese; 9 this.Math = math; 10 this.English = english; 11 } 12 public Student(string name, int chinese, int math, int english) 13 : this(name, 21, '男', chinese, math, english) 14 { 15 // 16 // 17 } 18 private string _name; 19 public string Name 20 { 21 get { return _name; } 22 set { _name = value; } 23 } 24 private int _age; 25 public int Age 26 { 27 get { return _age; } 28 set { _age = value; } 29 } 30 private char _gender; 31 public char Gender 32 { 33 get { return _gender; } 34 set { _gender = value; } 35 } 36 private int _chinese; 37 public int Chinese 38 { 39 get { return _chinese; } 40 set { _chinese = value; } 41 } 42 private int _math; 43 public int Math 44 { 45 get { return _math; } 46 set { _math = value; } 47 } 48 private int _english; 49 public int English 50 { 51 get { return _english; } 52 set { _english = value; } 53 } 54 public void SayHello() 55 { 56 Console.WriteLine("我是{0},{1}岁,是{2}生,语文{3}分,数学{4}分,英语{5}分", this.Name, this.Age, this.Gender, this.Chinese, this.Math, this.English); 57 } 58 } 59 60 61 //Main函数中调用: 62 Student s = new Student("刘培群", 100, 90, 60); 63 s.SayHello(); 64 65 //输出: 66 我是刘培群,21岁,是男生,语文100分,数学100分,英语100分
值类型和引用类型:
值类型:int、double、bool、char、decimal、struct、enum
引用类型:类(string、自定义类 等)、数组、接口、委托
区别:
1)值类型和引用类型在内存上存储的地方不一样。存储:值类型的值存储在内存的栈中;引用类型的值存储在内存的堆中,而引用存储在内存的栈中。
2)值类型和引用类型的传递方式不一样。值类型为值传递,引用类型为引用传递。
静态成员 与 非静态成员:
成员调用:
1)在调用对象成员时要使用“实例.实例成员”
2)在调用静态成员时要使用“类名.静态成员”
总结:
1)静态成员使用类名调用,实例成员使用对象调用。
2)静态函数中,只能访问静态成员,不允许访问实例成员。
3)实例函数中,既可以使用静态成员,也可以使用实例成员。
4)静态类在整个项目中资源共享。
5)如果你想要你的类当做一个“工具”去使用,这个时候可以考虑将类写成静态类。
6)类、字段、属性、方法都可以是静态的。
7)在非静态类中即可以有静态成员,也可以有非静态成员。
继承:类能够从他的父类中继承继承其所有功能。
方式:
1)类继承。类继承只允许单一继承,即只有一个基类,单一继承能够有效降低复杂性,无法调用基类的私有成员。
2)接口继承。接口继承允许多重继承。
使用类继承的情况:
1)派生类与基类的关系是“属于”而不是“具有”关系,“具有”关系的类不适合使用类继承,因为这样会继承不适当的属性和的方法。
2)可以重用基类的代码。
3)需要将相同的类和方法用到不同的数据类型。
4)类层次的结构相当浅。
5)需要通过更改基类来对派生类进行全局更改。
继承的特性:
1)继承的单根性:一个派生类只能有一个基类。
2)继承的传递性;
派生类继承基类时包括:首先,派生类继承了基类的属性和方法,但是派生类并没有继承基类的私有字段
问:派生类有没有继承基类的构造函数?
答:子类并没有继承基类的构造函数,但是派生类会默认调用基类无参数的构造函数,创建基类对象,让派生类可使用基类中的成员。
所以如果在基类中重新写了一个有参数的构造函数之后,那个无参的被干掉了。
解决方法:
1)在基类中重新写一个无参数的构造函数(注:一般不去这么做)
2)在派生类中显式的调用基类的构造函数,用法是使用关键字base,用法 :base( )
例:
1 public class Person 2 { 3 public Person(string name, int age, char gender) 4 { 5 this.Name = name; 6 this.Age = age; 7 this.gender = gender; 8 } 9 private string _name; 10 public string Name 11 { 12 get { return _name; } 13 set { _name = value; } 14 } 15 private int _age; 16 public int Age 17 { 18 get { return _age; } 19 set { _age = value; } 20 } 21 private char gender; 22 public char Gender 23 { 24 get { return gender; } 25 set { gender = value; } 26 } 27 public void SayHello() 28 { 29 Console.WriteLine("大家好!"); 30 } 31 } 32 public class Student : Person 33 { 34 public Student(string name, int age, char gender, int id) 35 : base(name, age, gender) 36 { 37 this.Id = id; 38 } 39 // private string _name; 40 // public string Name 41 // { 42 // get { return _name; } 43 // set { _name = value; } 44 // } 45 // private int _age; 46 // public int Age 47 // { 48 // get { return _age; } 49 // set { _age = value; } 50 // } 51 // private char gender; 52 // public char Gender 53 // { 54 // get { return gender; } 55 // set { gender = value; } 56 // } 57 private int _id; 58 public int Id 59 { 60 get { return _id; } 61 set { _id = value; } 62 } 63 //public void SayHello() 64 //{ 65 // Console.WriteLine("大家好!"); 66 //} 67 public void Study() 68 { 69 Console.WriteLine("我是学生,我可以学习"); 70 } 71 } 72 public class Teacher : Person 73 { 74 public Teacher(string name, int age, char gender, double salary) 75 : base(name, age, gender) 76 { 77 this.Salary = salary; 78 } 79 //private string _name; 80 //public string Name 81 //{ 82 // get { return _name; } 83 // set { _name = value; } 84 //} 85 //private int _age; 86 //public int Age 87 //{ 88 // get { return _age; } 89 // set { _age = value; } 90 //} 91 //private char gender; 92 //public char Gender 93 //{ 94 // get { return gender; } 95 // set { gender = value; } 96 //} 97 private double _salary;//工资 98 public double Salary 99 { 100 get { return _salary; } 101 set { _salary = value; } 102 } 103 //public void SayHello() 104 //{ 105 // Console.WriteLine("大家好!"); 106 //} 107 public void Teach() 108 { 109 Console.WriteLine("我是老师,我可以教学"); 110 } 111 } 112 113 //Main函数中调用: 114 Student ss = new Student("刘培群", 21, '男', 0021); 115 ss.SayHello(); 116 ss.Study(); 117 Console.WriteLine("我的学生ID是 {0}", ss.Id); 118 119 Teacher tt = new Teacher("老李", 30, '男', 5000); 120 tt.SayHello(); 121 tt.Teach(); 122 Console.WriteLine("我的教师工资是{0}", tt.Salary); 123 124 //输出: 125 126 大家好! 127 我是学生,我可以学习 128 我的学生ID是 0021 129 大家好! 130 我是老师,我可以教学 131 我的教师工资是5000
多态:指一个类可以具有多种形态(类型)。或者可以理解成为一个类或接口提供多种不同的行为。
多态性指的是:定义具有相同名称的方法和属性的多个类,这些类具有不同的行为,但共享相同的基类或接口。
C#语言中支持两种类型的多态性:
1)基于继承的多态性。
2)基于接口的多态性。
大多数面向对象的编程语言通过继承来实现多态性,是指在基类中定义方法并在派生类中使用新的实现重写他。
多态和重写是紧密相关的。
实现多态的三种方式:
♦虚方法(virtual修饰符):
虚方法使用修饰符virtual修饰,派生类在继承时可以重写它,派生类重写使用override修饰符。
♦抽象类(abstract修饰符):类的抽象概述,仅提供类的定义,而不提供类的具体实现细节。抽象类的实现是由派生类完成的。
当基类的方法不知道如何去实现的时候,可以考虑将基类写成抽象类,将方法写成抽象方法。使用修饰符abstract修饰。
类中只要有一个方法声明为抽象方法,这个类也必须声明为抽象类。
抽象类特性:
1)抽象类不能被实例化。
2)抽象类可以包含抽象方法和抽象的访问器。
3)在方法或属性声明中使用了abstract修饰符修饰,以指示方法和属性不包含实现。
4)不能用sealed修饰符修改抽象类。
5)从抽象类派生的非抽象类,必须包括继承的所有抽象方法和抽象访问器的实现。
抽象方法特性:
1)抽象方法是隐式的虚方法。
2)只允许在抽象类中使用抽象方法声明。
3)因为抽象方法声明中不提供实际的实现,所以没有方法体,方法声明是以一个分号结束,并且在签名后没有大括号{ }。
4)实现是由一个重载方法提供的,此重载方法是非抽象类的成员。
5)在抽象方法声明中使用static和virtual修饰符是错误的。
设计和声明抽象类需注意:
1)不要在抽象类中定义公共(public)或内部保护(protected internal)的构造函数。
2)如果需要构造函数,应该在抽象类中声明为受保护的(protected)或者是内部的(internal)构造函数。
3)应该具体提供一个该抽象类的具体实现。
4)在抽象类中并不是所有的方法都必须是抽象方法。
♦接口(interface修饰符):接口是一种规范,提供一份协议。与抽象类相似,接口不能被实例化。与抽象类不同的是,接口并不提供任何实现。
注:.NET约定,定义接口时以I作为接口名称的前缀,以able作为接口名称的后缀。如:IComputerable。
接口具有以下特性:
1)接口类似于抽象类,继承接口的任何非抽象类型都必须实现接口的所有成员。
2)不能直接实例化接口
3)接口可以包含事件、索引器、方法和属性。
4)接口不包含方法的实现。
6)接口可以继承多个接口。(注:接口不能包含字段,接口成员一定是公共的。)
8)类或结构可以继承多个接口,实现类似于C++中的多继承。
7)类和结构可以从接口继承,但是与类继承不同。
9)当类或结构继承接口时,它继承成员定义但不继承成员实现。
委托(delegate):委托是一种数据结构,是完全的面向对象且使用安全的类型。类似C++的指针,不同的是C++的是指针只能指向静态的方法,而委托除了能指向静态的方法外,还能指向对象实例的方法。
特点:
1)不知道且不关心自己引用的对象的类。
2)任何对象中的方法都可通过委托动态的调用,只是方法的参数类型和返回类型必须与委托的参数类型和返回类型相匹配。这使得委托完全适合匿名调用。
适用方面:
1)回调(CallBack)机制。
2)事件处理。
匿名函数:方法只需要用到一次时可以使用,用委托的关键字delegate指示。
例:
public delegate void DelSayHi(string name);
DelSayHi del = delegate(string name)
{
Console.WriteLine("你好" + name);
};
del("张三");
lamda表达式:匿名函数的一种简写方式。
例:
public delegate void DelSayHi(string name);
DelSayHi del = (string name) => { Console.WriteLine("你好" + name); };
del("张三");
注:(string name) 处表示释放出参数,没有参数时括号也不能省略。
泛型委托:
例:求数组最大值或最大长度
1 public delegate int DelCompare<T>(T o1, T o2); 2 static void Main(string[] args) 3 { 4 int[] nums = { 5, 4, 8, 2, 4, 6, 1, 9, 23, 12, 15, 5, 3 }; 5 string[] strs = { "2dhi", "5d4dg5", "灯市口军6", "宿舍109" }; 6 int i = GetMax<int>(nums, Compare); 7 string s = GetMax<string>(strs, (string s1, string s2) => 8 { 9 return s1.Length - s2.Length; 10 }); 11 Console.WriteLine(i); 12 Console.WriteLine(s); 13 Console.ReadKey(); 14 } 15 public static T GetMax<T>(T[] nums, DelCompare<T> del) 16 { 17 T max = nums[0]; 18 for (int i = 0; i < nums.Length; i++) 19 { 20 if (del(max, nums[i]) < 0)//max < nums[i] 21 { 22 max = nums[i]; 23 } 24 } 25 return max; 26 } 27 public static int Compare(int n1, int n2) 28 { 29 return n1 - n2; 30 }
多播委托:
例:
1 public delegate void DelTest(); 2 static void Main(string[] args) 3 { 4 DelTest delTest = T1; 5 delTest += T2; 6 delTest += T3; 7 delTest(); 8 Console.ReadKey(); 9 } 10 public static void T1() 11 { 12 Console.WriteLine("My naem is T1"); 13 } 14 public static void T2() 15 { 16 Console.WriteLine("My naem is T2"); 17 } 18 public static void T3() 19 { 20 Console.WriteLine("My naem is T3"); 21 }
事件(event保留字):指当对象发生某些事情时,向其他对象提供通知的一种方法。C#中事件是通过委托(delegate)实现的。
事件有两个角色:一个是事件发送方,指接触事件的对象。一个是事件接收方,指注册想在某种事件发生时被通知的对象。
字符串知识:
字符串具有不可逆性
字符与字符串之间的转换:
ToCharArray():可以将字符串转换为字符数组,调用字符串的ToCharArray()方法
new string(char [] chs):能够将char数组转换为字符串,初始化一个字符串时顺便使用
常用方法:
.Length //获取当前字符中字符的个数
.ToUpper() //转换为大写
.ToLower() //转换为小写
.Equals(string str, StringComparison.OrdinalIgnoreCase) //忽略大小写
.Split() //分割字符串,如:.Split(char chs, StringSplitOptions.RemoveEmptyEntries)
.Contains(string str) //检索字符串中是否包含指定子串
.Replace(string oldstr, string newstr) //替换字符串
.Substring() //截取字符串
.StartsWith(string str) //判断是否以目标字符串开头
.EndsWith(string str) //判断是否以目标字符串结尾
.IndexOf() //返回查找的目标字符串的第一个匹配索引,可从指定索引开始找
.LastIndexOf() //返回查找的目标字符串的最后一个匹配索引,可从指定索引开始找
.Trim() //移除字符串首尾空白
.TrimStart() //移除字符串首部空白
.TrimEnd() //移除字符串尾部空白
string.IsNullOrEmpty(string str) //判断字符串是否为空或空字符串
string.Join(string strFenGeFu, string strStr) //用指定字符分隔字符串数组各元素,并串联起来
里氏转换:
1)子类可以赋值给父类。
2)如果父类中装的是子类对象,那么可以将这个父类转换为子类对象。
注:子类对象可以调用父类成员,但父类对象永远只能调用自己的成员。
转换:
1)is 用法:表示类型转换,如果能够转换成功,则返回一个true,否则返回一个false
2)as 用法:表示类型转换,如果能够转换成功则返回对应的对象,否则返回一个null
枚举 的相关转换规则:
可以将一个枚举类型的变量与一个int类型或者string类型的变量相互转换。
枚举与int类型相互转换:
1)枚举类型与int类型相互兼容,可以通过强制类型转换的语法相互转换。
2)当转换的值,枚举中没有的时候,不会抛异常,而是直接将数字显示出来。
枚举与string类型相互转换:
1)枚举转换成string类型,直接使用 .ToString()
2)string类型转换成枚举需要使用类似的格式: (要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型),"要转换的字符串");
3)如果转换的字符串是数字,就算枚举中没有也不会抛异常;若转换的是文本,枚举中没有则抛异常。
绝对路径 和 相对路径:
绝对路径:通过给定路径能直接在我的电脑中找到这个文件。
相对路径:文件对于应用程序的路径。
装箱 和 拆箱:值类型和引用类型相互转换的过程。
装箱:将值类型隐式转换为object类型,或者转换为该值类型实现了的接口类型。(效率慢,尽量避免装箱)
拆箱:将object类型显式转换为值类型,或者把值类型实现了的接口类型转换为该值类型。(拆箱操作包括两个步骤:1>是检查对象实例,确认它是给定值类型的包装数值。2>是把实例中的值复制到值类型的变量中)
判断两种类型是否发生转向或者拆箱,要看这两种类型是否存在继承关系。若存在继承关系有可能发生转向或拆箱,反之则不可能发生装箱或者拆箱。
几个集合:
ArrayList 数组列表,集合
Hashtable 哈希表
List 列表
Dictionary 字典
ArrayList 类(System.Collections.ArrayList):数组列表(数组集合)
数组集合,表示很多数组的一个集合。长度可以任意改变,类型随意。
我们打印一个对象输出到控制台,默认情况下,打印的就是这个对象所在的类的命名空间。
ArrayList集合的长度问题:
每次集合中实际包含的元素个数(count)超过了可以包含的元素的个数(capacity)的时候,集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。
ArrayList 类常用属性:
.Count//实际包含的元素数
.Capacity//可包含的元素数
.ArrayList 类常用方法:
.Add()//添加非数组元素
.AddRange()//添加数组元素
.Clear()//移除所有元素
.Remove()//移除单个元素
.RemoveAt()//移除指定索引处的元素
.TrimToSize()//将容量设置为数组集合中元素的实际数目
.RemoveRange()//根据下标移去一定范围元素
.Sort()//升序
.Reverse()//反转
.Insert()//在指定位置插入一个元素
.InsertRange()//在指定位置插入一个集合中的某个元素
.IndexOf()//返回列表中首个匹配项的从0开始的索引
.LastIndexOf()//返回列表中最后一个匹配项的从0开始的索引
.Contains()//判断是否包含某个元素
Hashtable 类(System.Collections): 哈希表(键值对集合)
Hashtable 类常用属性:
.Count//键值对的数目
.Kes//键集合
.Values//值集合
Hashtable 类常用方法:
.Add(key,value)//添加键值对
.Clear()//移除所有元素
.Remove(key)//根据键键移除键值对
.ContainsKey(key)//是否包含键
遍历哈希表可以用到DictionaryEntry类,代码如下:
foreach(DictionaryEntry de in ht) //ht为一个Hashtable实例
{
Console.WriteLine(de.Key); //de.Key对应于keyvalue键值对key
Console.WriteLine(de.Value); //de.Key对应于keyvalue键值对value
}
遍历键:
foreach (int key in hashtable.Keys)
{
Console.WriteLine(key);
}
遍历值:
foreach (string value in hashtable.Values)
{
Console.WriteLine(value);
}
对哈希表进行排序:
对哈希表按key值重新排列的做法:
ArrayList akeys=new ArrayList(ht.Keys);
akeys.Sort(); //按字母顺序进行排序
foreach(string key in akeys)
{
Console.WriteLine(key + ": " + ht[key]); //排序后输出
}
List 类(System.Collections.List): 列表
列表泛型集合,表示很多数组的一个集合。长度可以任意改变,类型固定。
ArrayLis 类有的基本方法和属性都有。
.ToArray()//将泛型数组的元素复制到新数组中,返回数组的数组类型视泛型数组的具体类型而确定。
有时会与数组的.ToList()方法进行相关操作,来帮助创建一个List类对象。
Dictionary 类(System.Collections.Generic):字典
Hashtable 类有的基本方法和属性都有。
遍历Dictionary类的对象的元素的例子:
Dictionary<int, string> d = new Dictionary<int, string>();
//其一:(注:KeyValuePair结构定义可设置或检索的键/值对)
foreach (KeyValuePair<int, string> kv in d) {
Console.WriteLine("{0}--{1}", kv.Key, kv.Value); }
//其二
foreach (var item in d.Keys) {
Console.WriteLine("{0}--{1}", item, d[item]); }