9.5 约束
有的时候,您必须确保添加进泛型列表中的元素拥有某些约束(例如,它们从一个给定的基类继承或它们实现了指定的接口)。在下面的例子中,我们实现了一个简单的可排序的单链表。链表由多个Node组成,每个Node必须保证添加进去的元素实现了IComparer接口。您可以这样声明:
public class Node<T> : IComparable<Node<T>> where T : IComparable<T>
这句代码定义了一个泛型Node,它操作类型T。Node中的T实现了IComparable<T>接口,这意味着两个Node的T可以进行对比。Node类通过约束(where T : IComparable<T>)来操作那些实现了IComparable接口的类型。因此您可以使用任何类型来替代T,只要那种类型实现了IComparable接口
例9-12举例说明了完整的接口实现,并进行分析如下。
例9-12 使用约束
using
System;
using
System.Collections.Generic;
namespace
UsingConstraints
{
public
class
Employee : IComparable
<
Employee
>
{
private
string
name;
public
Employee(
string
name)
{
this
.name
=
name;
}
public
override
string
ToString()
{
return
this
.name;
}
//
实现接口
public
int
CompareTo(Employee rhs)
{
return
this
.name.CompareTo(rhs.name);
}
public
bool
Equals(Employee rhs)
{
return
this
.name
==
rhs.name;
}
}
//
节点必须实现Node<T>的IComparable接口。
//
通过where关键字约束Node只接收实现了IComparable接口的项
public
class
Node
<
T
>
: IComparable
<
Node
<
T
>>
where
T : IComparable
<
T
>
{
//
成员变量
private
T data;
private
Node
<
T
>
next
=
null
;
//
下一个节点
private
Node
<
T
>
prev
=
null
;
//
前一个节点
//
构造方法
public
Node(T data)
{
this
.data
=
data;
}
//
属性
public
T Data
{
get
{
return
this
.data; }
}
public
Node
<
T
>
Next
{
get
{
return
this
.next; }
}
public
int
CompareTo(Node
<
T
>
rhs)
{
//
这样使用是因为约束
return
data.CompareTo(rhs.data);
}
public
bool
Equals(Node
<
T
>
rhs)
{
return
this
.data.Equals(rhs.data);
}
//
方法
public
Node
<
T
>
Add(Node
<
T
>
newNode)
{
//
下面的“我”代表类的当前实例
if
(
this
.CompareTo(newNode)
>
0
)
//
小于我则放在我前面
{
newNode.next
=
this
;
//
新节点的下一节点指向我
//
如果我有前一个节点,则新节点的前一个节点指向它
//
新节点做为它的下一个节点
if
(
this
.prev
!=
null
)
{
this
.prev.next
=
newNode;
newNode.prev
=
this
.prev;
}
//
设置我的前一个节点为新节点
this
.prev
=
newNode;
//
从下面的LinkedList<T>代码可以得知,添加都是从
//
头节点开始判断,只有新节点为头节点时才返回它
return
newNode;
}
else
//
大于等于我则放在我后面
{
//
如果我有下一个,则跟下一个进行对比
//
这里使用了递归,直到新节点找到比它大的节点为止
if
(
this
.next
!=
null
)
{
this
.next.Add(newNode);
}
//
如果我没有下一个节点,则设置新节点为我的下一个
//
节点,并把它的上一个节点指向我
else
{
this
.next
=
newNode;
newNode.prev
=
this
;
}
return
this
;
}
}
public
override
string
ToString()
{
string
output
=
data.ToString();
if
(next
!=
null
)
{
//
这里也使用了递归打印链表上的所有元素
output
+=
"
,
"
+
next.ToString();
}
return
output;
}
}
public
class
LinkedList
<
T
>
where
T : IComparable
<
T
>
{
//
成员变量
private
Node
<
T
>
headNode
=
null
;
//
索引器
public
T
this
[
int
index]
{
get
{
//
由于是链表,这里需要从头遍历
int
ctr
=
0
;
Node
<
T
>
node
=
headNode;
while
(node
!=
null
&&
ctr
<=
index)
{
if
(ctr
==
index)
{
return
node.Data;
}
else
{
node
=
node.Next;
}
++
ctr;
}
throw
new
ArgumentOutOfRangeException();
}
}
//
默认构造方法
public
LinkedList()
{
}
//
方法
public
void
Add(T data)
{
if
(headNode
==
null
)
{
headNode
=
new
Node
<
T
>
(data);
}
else
{
headNode
=
headNode.Add(
new
Node
<
T
>
(data));
}
}
public
override
string
ToString()
{
if
(
this
.headNode
!=
null
)
{
return
this
.headNode.ToString();
}
else
{
return
string
.Empty;
}
}
}
//
测试类
class
Test
{
static
void
Main()
{
//
创建一个实例来进行方法
Test t
=
new
Test();
t.Run();
}
public
void
Run()
{
LinkedList
<
int
>
myLinkedList
=
new
LinkedList
<
int
>
();
Random rand
=
new
Random();
Console.Write(
"
Adding:
"
);
for
(
int
i
=
0
; i
<
10
; i
++
)
{
int
nextInt
=
rand.Next(
10
);
Console.Write(
"
{0}
"
, nextInt);
myLinkedList.Add(nextInt);
}
LinkedList
<
Employee
>
employees
=
new
LinkedList
<
Employee
>
();
employees.Add(
new
Employee(
"
John
"
));
employees.Add(
new
Employee(
"
Paul
"
));
employees.Add(
new
Employee(
"
George
"
));
employees.Add(
new
Employee(
"
Ringo
"
));
Console.WriteLine(
"
\nRetrieving collections
"
);
Console.WriteLine(
"
Integers:
"
+
myLinkedList);
Console.WriteLine(
"
Employees:
"
+
employees);
}
}
}
运行结果:
Adding:2 1 2 6 1 5 9 0 5
Retrieving collections…
Integers:0,1,1,1,2,2,5,5,6,9
Employees:George, John, Paul, Ringo
本例的开头声明了一个可以放到链表中的类:
public class Employee : IComparable<Employee>
这个声明指出Employee对象是可以进行对比的,可以看到Employee类实现了CompareTo和Equals方法。注意:这些方法是类型安全的(从参数传递进去的类是Employee类型)。LinkedList本身声明为只操作实现了IComparable接口的类型:
public class LinkedList<T> where T : IComparable<T>
这样就可以保证对列表进行排序。LinkedList操作Node类型的对象。Node也实现了IComparable接口,并要求它本身所操作的数据也实现了IComparable接口:
public class Node<T> : IComparable<Node<T>> where T : IComparable<T>
这些约束使得Node实现CompareTo方法变得安全而简单,因为Node知道它将和其它Node的数据进行对比:
public int CompareTo(Node<T> rhs)
{
// 这样使用是因为约束
return data.CompareTo(rhs.data);
}
注意,我们不需要测试
rhs
从而得知它是否实现了
IComparable
接口;我们已经约束了
Node
只能操作实现了
IComparable
接口的数据。