在平时的项目中,许多东西都是用到了才看,甚至是只用不看。对下面几个知识点进行总结,加深自己对C#的理解。
今天刚好看到一篇讲C#数据结构很不错的博文,就不转载了。
贴上网站:http://www.cnblogs.com/gaochundong/p/data_structures_and_asymptotic_analysis.html#hashtable
1.Array,ArrayList,List<T>,LinkedList<T>,Queue<T>,Stack<T>,Dictionary<K,T>
(1)Array
(2)ArrayList
A.在System.Collections命名空间下,不必指定长度,存储空间会根据需要动态增减。
B.可以存储不同的数据类型,都视为Object;导致不是类型安全,并且有频繁的装箱拆箱操作,造成性能和效率上的影响。
C.每次增加新元素时,会检查内部存储空间是否不够,是否以当前容量两倍进行扩容,将旧元素复制到新空间,然后删除旧空间。
D.若了解数据量范围,可以指定空间大小,比如 ArrayList List = new ArrayList( 200);是开辟200个存储空间。ArrayList List = new ArrayList();
则按系统默认容量进行初始化,网上有称为默认容量为16,具体没去考证过。
(3)List<T>
(4)Queue<T>和Stack<T>LinkedList<T>
(5)Dictionary<K,T>
字典的实现方式就是哈希表的实现方式,不过字典是类型安全的,定义时要声明key和item数据类型。通过K来“检索”T。
另外还有SortedList/HashSet等~名称上有Hash的都是基于hash的,比如HashSet<T>不能按索引访问,不能有重复数据,其Contains()方法为O(1),而
List的为O(n)。list 就是强化版本array, 很多情况下list的功能是不可替代的 所以 在集合的目的是为了检索的情况下,我们才应该使用HashSet<T>代替List<T>。
hash code会改变的对象不适合放在hash表中 所以也要少为注意下
2.委托和事件
(1)委托
using System;
using System.Collections.Generic;
using System.Text;
namespace Delegate {
//定义委托,它定义了可以代表的方法的类型
public delegate void GreetingDelegate(string name);
class Program {
private static void EnglishGreeting(string name) {
Console.WriteLine("Morning, " + name);
}
private static void ChineseGreeting(string name) {
Console.WriteLine("早上好, " + name);
}
//注意此方法,它接受一个GreetingDelegate类型的方法作为参数
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {
MakeGreeting(name);
}
static void Main(string[] args) {
GreetPeople("Jimmy Zhang", EnglishGreeting);
GreetPeople("张子阳", ChineseGreeting);
Console.ReadKey();
}
}
}
输出如下:
Morning, Jimmy Zhang
早上好, 张子阳
static void Main(string[] args) {
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
//注意是=号,因为要先赋值//也可以在定义时实例化,即GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法//已经赋值过,使用+=进行多个方法的绑定
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
GreetPeople("Jimmy Zhang", delegate1);
Console.ReadKey();
}
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
也可以绕过GreetPeople方法,通过委托来直接调用EnglishGreeting和ChineseGreeting
static void Main(string[] args) {
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
delegate1 ("Jimmy Zhang");
Console.ReadKey();
}
再看看以下代码:
public class GreetingManager{
//在GreetingManager类的内部声明delegate1变量
public GreetingDelegate delegate1;
public void GreetPeople(string name) {
if(delegate1!=null){ //如果有方法注册委托变量
delegate1(name); //通过委托调用方法
}
}
}
static void Main(string[] args) {
GreetingManager gm = new GreetingManager();
gm.delegate1 = EnglishGreeting;
gm.delegate1 += ChineseGreeting;
gm.GreetPeople("Jimmy Zhang"); //注意,这次不需要再传递 delegate1变量
}
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
(2)事件
委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
事件的命名为 委托去掉 EventHandler之后剩余的部分。
继承自EventArgs的类型应该以EventArgs结尾。
3.接口和抽象类
为什么要有接口?CLR via C#中说道,CLR不支持多继承,所以通过接口(interface)提供了“缩水版”的多继承,接口的方法必须显示实现。
(1)接口
目的:接口的主要目的是为不相关的类提供通用的处理服务,由于C#中只允许树形结构中的单继承,即一个类只能继承一个父类。
声明:接口声明的方式与声明类的方式相似,但使用的关键字是interface,而不是 class。接口只包含方法、属性、索引器和事件的签名。
(2)抽象类
(3)对比
相同点
1. 都不能被直接实例化,都可以通过继承实现其抽象方法。
2. 都是面向抽象编程的技术基础,实现了诸多的设计模式。
不同点
1. 接口支持多继承;抽象类不能实现多继承。抽象类可以有构造方法,接口中不能有构造方法。
2. 接口只能定义抽象规则;抽象类既可以定义规则,还可能提供已实现的成员。
3. 接口是一组行为规范;抽象类是一个不完全的类,着重族的概念。
4. 接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。
5. 接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法。
6. 接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。
7. 一个类可以实现多个接口,但只能继承一个抽象类。
8. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
9. 抽象类中可以包含静态方法,接口中不能包含静态方法
10.抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认为public abstract类型。
知乎上看到一个例子:
一个类继承了某个抽象类表示它“是什么”,实现了某个接口表示它“有什么功能”或者“会做什么事”。比如:燕子(具体类)是鸟(抽象类),会飞(接口)。
C#中不支持多继承,即燕子只能是鸟,不会是其他东西了;但可以有多个功能,做很多事,比如会飞(IFly),会吃(IEat)。