C#相关概念(ArrayList,List,LinkedList,Dictionary、委托和事件、接口和抽象类)

在平时的项目中,许多东西都是用到了才看,甚至是只用不看。对下面几个知识点进行总结,加深自己对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

最常用的数据结构,顺序存储相同数据类型的内容,可以通过下标O(1)进行访问。数组定义使用需要显示指定数组长度,不能更改。
一般定义为:
int[] arr1=new int[5];
int[,] arr2=new int [5,6]; //矩形数组

int[][] arr3 = new int[3][];//锯齿形
arr3[0]=new int[4];
arr3[1 ]=new int[5 ];
arr3[2 ]=new int[6 ];

(2)ArrayList

A.在System.Collections命名空间下,不必指定长度,存储空间会根据需要动态增减。

B.可以存储不同的数据类型,都视为Object;导致不是类型安全,并且有频繁的装箱拆箱操作,造成性能和效率上的影响。

C.每次增加新元素时,会检查内部存储空间是否不够,是否以当前容量两倍进行扩容,将旧元素复制到新空间,然后删除旧空间。

D.若了解数据量范围,可以指定空间大小,比如 ArrayList List = new ArrayList( 200);是开辟200个存储空间。ArrayList List = new ArrayList();

则按系统默认容量进行初始化,网上有称为默认容量为16,具体没去考证过。

(3)List<T>

数据类型要求相同,类型安全,不需要频繁装箱拆箱。可视为是ArrayList的改进,底层实现与ArrayList一样,都使用Array。

(4)Queue<T>和Stack<T>LinkedList<T>

A.队列:先进先出
B.栈:先进后出
C.链表,存储不要求是内存顺序存储。与C++中的顺序存储跟链式存储类似~优缺点一样,这里不废话。

(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.委托和事件

做项目在用C#,由于一直没有完全搞清楚委托和事件,一直觉得很不开心。。。看了别人文章整理下。用得上的同学或者想学的同学好好看看下面转载地址的文章~
推荐阅读:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx,讲得很清晰,下面的例子也引用其中!

(1)委托

委托可以理解为C++中的函数指针,可以将方法作为参数传递。
现在,请看看这个范例的完整代码:

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
早上好, 张子阳
委托的声明方式和类却完全不同,这是怎么一回事?实际上,委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任何可以声明类的地方都可以声明委托。委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

可以将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。如果要将方法解绑就使用-=。
在这个例子中,语法如下:

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)事件

事件封装了委托类型的变量,使得在类的内部,不管你声明它是public还是protected,它总是private的。
在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
事件的声明与委托变量delegate的声明唯一的区别是多了一个event关键字。所以声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。

.Net Framework的编码规范:
委托类型的名称都应该以EventHandler结束。
委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
事件的命名为 委托去掉 EventHandler之后剩余的部分。
继承自EventArgs的类型应该以EventArgs结尾。


3.接口和抽象类

为什么要有接口?CLR via C#中说道,CLR不支持多继承,所以通过接口(interface)提供了“缩水版”的多继承,接口的方法必须显示实现。

(1)接口

定义:接口是一种约束形式,其中只包括成员定义,不包含成员实现的内容。
目的:接口的主要目的是为不相关的类提供通用的处理服务,由于C#中只允许树形结构中的单继承,即一个类只能继承一个父类。
接口是让一个类具有两个以上基类的唯一方式。
声明:接口声明的方式与声明类的方式相似,但使用的关键字是interface,而不是 class。接口只包含方法、属性、索引器和事件的签名。
方法的实现是在实现接口的类中完成的。接口类型名称要用大写字母I开头,目的是方便在源代码中辨认接口类型。
接口可以继续其他的接口。接口方法要求显示标记为virtual,否则就会标记成virtual和sealed,这就会阻止派生类重写接口方法。这样就需要
重新继续同一个接口,并为该接口方法提供它自己的实现。

(2)抽象类

含有abstract修饰符的class即为抽象类,抽象类是特殊的类,只是不能被实例化,可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例;
除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。
另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖。

(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)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值