【C#与.NET程序设计】(7)- C#泛型、委托

泛型

  • 基本形式
using namespace System.Collections.Generic

// 泛型基本形式(以List为例)
List<ClassName> obj = new List<ClassName>();
  • 能用泛型就不要用非泛型
    非泛型主要有2个问题:
    1. 由于装箱/拆箱 造成的效率低下
    2. 不是类型安全的

一个例子(《C# 之泛型详解》

// 非泛型的处理方式

// 一个只能处理 int 的栈
public class Stack
{
    private int[] m_item;
    public int Pop(){...}
    public void Push(int item){...}

    public Stack(int i)
    {
        this.m_item = new int[i];
    }
}

// 为了能够处理不同类,定义为 object
public class Stack
{
    private object[] m_item;
    public object Pop(){...}
    public void Push(object item){...}

    public Stack(int i)
    {
        this.m_item = new[i];
    } 
}

可以看到,经过改造的 Stack 虽然变得灵活,但
1. 特定类型到 object 的转换(装箱/拆箱)会造成效率低下
2. 由于 object 是所有类的基类,任何类型输入都可以成功编译,但因为类型不支持,可能会造成运行时错误

泛型的处理方式

// 泛型的处理方式
public class Stack<T>
{
    private T[] m_item;
    public T Pop(){...}

    public void Push(T item){...}
    public Stack(int i)
    {
        this.m_item = new T[i];
    }
}

// 使用具体的类型实例化该类即可
Stack<int> a = new Stack<int>(100);
a.Push("a string");    // 编译不通过

支持泛型的类/接口

使用命名空间:System.Collections.Generic

名称属性作用
ICollection< T > 接口 所有泛型集合类型定义基本特性
IComparer<T>接口对象比较
IDictionary< TKey,TValue >接口字典
IEnumerable< T > 接口 返回 IEnumerator
IEnumerator<T>接口允许泛型集合以 foreach 形式迭代
IList< T > 接口 列表
ISet<T>接口集合
Dictionary< TKey,TValue >泛型字典
List< T > 泛型列表
LinkedList<T>泛型双向链表
Queue< T > 泛型队列
SortedDictionary<TKey,TValue>排序的泛型字典
SortedSet< T > 排序的不重复的泛型集合
Stack<T>泛型栈

初始化语法

// 初始化 int 的泛型 List<>
List<int> myList = new List<int>{0, 2, 1, 3};

SortedSet< T > 类

该类的项是排序的,在插入和移除项后也能自动确保排序正确
对于自定义的类,需要实现接口 IComparer<T>

// sortedset<T>

// 自定义一个排序的类实现 IComparer 接口的 Compare 方法
class SortPerson : IComparer<Person>
{
    public int Compare(Person p1, Person p2)
    {
        // 根据年龄排序
        if(p1.Age > p2.Age)
            return 1;
        if(p1.Age < p2.Age)
            return -1;
        else
            reutrn 0;
    }
}

// 生成的 SortSet<Person> 可以根据 age 自动排序
// 注意 SortSet 的元素是 Person,类 SortPerson 只是实现一个排序规则
SortSet<Person> setOfPeople = new SortSet<Person>(new SortPerson())
{
    new Person {...},
    new Perosn {...},
    ...
};

创建自定义泛型

感觉和 C++ 里的模板很像,两者的差别以后再研究

// 泛型方法实现任意两个类型参数的 swap
static void Swap<T>(ref T a, ref T b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

使用 default 设置默认值:因为在实例化前不知道具体类型

// 泛型中 default 设置默认值
public void resetData<T>(ref T data)
{
    // default 可以根据 T 的实际类型设置默认值
    data = default(T);
}

委托(Delegate)

C#的委托类似于 C/C++ 的函数指针,一般用于事件和回调方法

相较于函数指针,委托具有以下两个优势

  • 函数指针只能指向静态函数,而委托类型既可引用静态函数,又可引用非静态成员
  • 与函数指针相比,委托类型是面向对象、类型安全的受控对象,无须担心地址越界

委托类型的定义

// 使用关键字 delegate 声明委托类型
public delegate void BuyTicketEventHandle(int num);
  • 可以把委托看成是一个命令(参考文章大白话系列之C#委托与事件讲解(一)),以上语句声明了一个输入为 int 输出为 void 的命令
  • 编译器会自动生成一个派生自 System.MulticastDelegate 的密封类。因此实际上 BuyTicketEventHandle 是一个类

委托类型的使用

// 委托

// 执行类
public class buybuybuy
{
    public static void BuyTicket(int num)
        Console.WriteLine("{0} tickets bought");
}

// 命令类
public class order
{
    // 声明一个委托
    public delegate void BuyTicketEventHandle(int num);

    public static void Main(string[] args)
    {
        // 绑定执行函数到委托类
        BuyTicketEventHandle BuyTickets = new BuyTicketEventHandle(buybuybuy.BuyTicket);

        BuyTickets(3);
    }
}

委托的多路广播(委托链)

可以理解为一个命令链,给一个委托对象添加多个方法(当然输入输出形式是一样的)

// 委托的多路广播
// 使用 += 和 -=
// 实际上调用的是 Delegate.Combine() 和 Delegate.Remove()

// 执行类
public class buybuybuy
{
    public static void BuyTicket(int num){
        Console.WriteLine("{0} tickets bought", num);
    }

    public static void SellTicket(int num){
        Console.WriteLine("{0} tickets sold", num);
    }
}

// 命令类
public class order
{
    // 声明一个委托
    public delegate void BuyTicketEventHandle(int num);

    public static void Main(string[] args)
    {
        // 绑定执行函数到委托类
        BuyTicketEventHandle ListBuyTickets = new BuyTicketEventHandle(buybuybuy.BuyTicket);

        // 添加另一个方法
        ListBuyTickets += buybuybuy.SellTicket;
        // 会依次执行 BuyTicket(3)、SellTicket(3)
        ListBuyTickets(3);

        // 删掉一个方法
        ListBuyTickets -= buybuybuy.BuyTicket;
        // 仅执行 SellTicket(4)
        ListBuyTickets(4);

        Console.ReadKey();
    }
}

可以看到,这里有一个问题,由于方法 BuyTicket 和 SellTicket 是带参数的
因此链式执行只能使用同一组参数
所以在设计的时候,尽量将委托设为 void ,作为调用函数的输入参数


参考

【1】C#与.NET 4高级程序设计(第5版)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值