关于C#泛型编程杂记[引用]

泛型的特点:
1.增加了编译时的类型检查.
2.减少了装箱和拆箱操作.
3.减少了运行时的类型检查.

.NET Framework 2.0支持了泛型,在它给我们带来性能收益和强类型的方便的同时,由于接口、委托等元素对泛型的支持,一些前所未有的泛型设计和算法也逐渐展现出来

C# 2.0支持了一种叫“匿名方法”的特性,它与泛型委托结合之后,能够产生出许多诱人的新用法。比如,若想找出列表中所有大于10的元素,只需要如下:

List<int> l1 = new List<int>();
List<int> l2 = l1.FindAll(delegate(int i)...{ return i > 10; });
对于首次接触这个新特性的人来说,此语法较为费解。我们看看Generic List类的FindAll方法的定义:
public List<T> FindAll(
Predicate<T> match
);
我们看到,唯一的参数match是“Predicate<T>”类型的,那Predicate<T>又是什么呢,请看定义:
public delegate bool Predicate<T>(
T obj
);
这是一个委托,接受T类型的一个参数obj,并返回一个布尔类型,表示对obj的一个判断。现在我们就明白了,FindAll方法本应接受一个函数委托作为参数,以表示查找满足判断的所有元素,而C#的匿名方法则提供了一种就地提供函数委托的方法,使得委托的逻辑可以在同一条语句中表达。

如下例:
Racer类,定义了赛车手及车如:
public class Racer
{
string name,car;
public string Name
{
get{ return name; }
set{ name=value; }
}

public string Car
{
get{ return car; }
set{ car=value; }
}

public Racer(string name,string car)
{
this.name=name;
this.car=car;
}

public override string ToString()
{
return name+","+car;
}
}

此处的类FindRacer用于寻找相应的赛车所对应的赛手信息
public class FindRacer
{
string car;
public FindRacer(string car)
{
this.car=car;
}

public bool DrivingCarPredicate(Racer racer)
{
return racer.Car==car;
}
}

应用: 使用List<T>类的FindAll()方法,实例化一个谓词委托,这个谓词委托接收finder.DrivingCarPredicate方法,FindAll()方法返回一个List<Racer>类型的列表,再使用foreach
迭代所有返回的赛手,并在控制台上显示他们:
FindRacer finder=new FindRacer("Ferrari");
foreach(Racer racer in
racers.FindAll(new Predicate<Racer>(finder.DrivingCarPredicate)))
{
Console.WriteLine(racer);
}

例子二:
List<Book> thelib = Library.getbooks();
List<Book> founds = thelib.FindAll(delegate(Book curbook){
if (curbook.isbn.StartsWith("..."))
return true;
return false;
});


foreach (Book b in founds)
Console.WriteLine(b.isbn);

这段程序非常简单的展示给我们需要查找的信息,代码也非常的直接易懂。
内置的数据结构给了我们强大的算法支持,不过,能不能够为自定义的类定义类似的算法呢?

比如,如果我有一个自定义的Library类并没有使用List<Book>存储数据,
而是使用某种自定义的数据结构,我能不能也让用户使用类似的语法,
忽略存储细节的使用匿名委托来实现特定的算法呢?

答案当然是肯定的,而且在C#中实现这样的功能是非常的简单。

首先让我们看看FindAll中用到的匿名委托的原型
public delegate bool Predicate<T>(T obj);

很明显的,上面的代码等于注册了一个搜索的回调,而在List内部定义了某种遍历的机制,从而实现了一个漂亮的算法结构Closure。

看到了这些,我们就可以定义自己的算法结构了,首先,我们来定义了一个如下的类

public class MyVec<T>
{
public static MyVec<T> operator + (MyVec<T> a, T b)
{
a._list.Add(b);
return a;
}

public override string ToString()
{
StringBuilder builder = new StringBuilder();

foreach (T a in _list)
{
builder.Append(a.ToString());
builder.Append(",");
}

string ret = builder.Remove(builder.Length - 1, 1).ToString();

return ret;
}

public MyVec<T> findAll(Predicate<T> act)
{
MyVec<T> t2 = new MyVec<T>();

foreach(T i in _list)
if (act(i))
t2._list.Add(i);

return t2;
}

// 这是一个内部的对象
private List<T> _list = new List<T>();
}

这个类中包含了一个的List<T>结构,主要是为了证实我们的想法是否可行,
事实上,任何一个可以支持foreach遍历的结构都可以作为内置的数据存储对象。

下面是我们来测试这个实验类的代码:


static void Main(string[] args)
{
MyVec<int> a = new MyVec<int>();
a += 12;
a += 15;
a += 32;
MyVec<int> b = a.findAll(delegate(int x)
{
if (x > 20)
return true;
return false;
}
);

Console.WriteLine("vection original");
Console.WriteLine(a.ToString());
Console.WriteLine("vection found");
Console.WriteLine(b.ToString());
Console.ReadLine();
}

编译,执行,程序输出:

vection original
12,15,32
vection found
32
输出与预期的相同.

Predicate<T>仅仅是为了仿照系统的实现而采用的一个委托,事实上可以使用自己定义的任何委托作为回调的函数体。
通过使用IEnumberable接口,可以实现对任意结构的遍历,从而对任何数据结构定义强大的算法支持。


再说明:Predicate 泛型委托
表示定义一组条件并确定指定对象是否符合这些条件的方法.

命名空间:System
程序集:mscorlib(在 mscorlib.dll 中)

类型参数
T 要比较的对象的类型.
参数
obj 要按照由此委托表示的方法中定义的条件进行比较的对象.
返回值
如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false.
备注:
此委托由 ArrayList 类的几种方法使用,用于在集合中搜索元素.


例三:
假设有一个联系人列表 List<Contact>,联系人的定义如下:
class Contact
{
public string name;
...
}
现在我们要把这个列表中所有联系人的姓名拷贝到另外一个列表。你可能马上就动手写了出来:

List<Contact> c1;
List<string> c2=new List<string>();

foreach(Contact c in c1)
{
c2.Add(c.Name);
}

这是一段非常规矩的 C# 代码。在 .NET 2.0 中,有了范型和匿名委托,我们可以写出如下的完成相同功能的实现.
List<Contact> C1;
List<string> C2=C1.ConvertAll<string>(delegate(Contact c){return c.Name;});

显然这段代码比手工编写的 foreach 代码更简捷,在表达意图方面也显得更加清楚和直接。其中 ConvertAll 方法是一个范型方法,作用是将列表元素转换为指定类型的列表。原型为:
List<U> ConvertAll<U>(Converter<T, U> converter);
Converter<T, U> 是一个范型委托,指定了如何进行转换(类似 C++ 中的函数对象),原型为(T 为原始类型,U 为目标类型.
delegate U Converter<T, U>(T from);

原型描述:

delegate bool Predicate<T>(T obj);
访问集合时,对指定元素的断言(true 或 false)

delegate void Action<T>(T obj);
访问集合时,对指定元素做出特定动作

delegate int Comparison<T>(T x, T y);
比较两个元素

delegate U Converter<T, U>(T from);
把一个元素转换为另外一个,用于在两个集合之间拷贝元素

List<T> 提供了如下支持 Functional Programming 的方法:

int FindIndex(Predicate<T> match);

int FindIndex(int index, Predicate<T> match);

int FindIndex(int index, int count, Predicate<T> match);
找出第一个满足断言条件的元素的索引

int FindLastIndex(Predicate<T> match);

int FindLastIndex(int index, Predicate<T> match);

int FindLastIndex(int index, int count, Predicate<T> match);
找出最后一个满足断言条件的元素的索引

List<T> FindAll(Predicate<T> match);
找出所有满足断言条件的元素

Nullable<T> Find(Predicate<T> match);
找出第一个满足断言条件的元素

Nullable<T> FindLast(Predicate<T> match);
找出最后一个满足断言条件的元素

bool Exists(Predicate<T> match);
判断满足断言条件的元素是否存在

bool TrueForAll(Predicate<T> match);
判断是否所有的元素都满足断言条件

int RemoveAll(Predicate<T> match);
删除所有满足断言条件的元素,返回删除的元素数

void ForEach(Action<T> action);
类似 foreach 语句

void Sort(Comparison<T> comparison);
排序

List<U> ConvertAll(Converter<T, U> converter);
转换集合元素


其它代码示例如下:
List<int> inta = new List<int>();
inta.Add(100);
inta.Add(200);
inta.Add(300);
inta.Add(400);
List<string> str = new List<string>();
str = inta.ConvertAll<string>(delegate(int a){ return a.ToString(); });

int bb;
bb = inta.Find(new Predicate<int>(delegate(int a){ if (a == 200) return true;
return false; }));

foreach (string a in str)
{
Console.WriteLine(a);
}

Console.WriteLine("\n\n"+str[2]);
Console.WriteLine("hello,bb is:" + bb.ToString());
posted on 2008-07-01 17:19  RIVERSPIRIT  阅读(371)  评论(0编辑  收藏  举报