泛型--约束

 

9.5 约束

有的时候,您必须确保添加进泛型列表中的元素拥有某些约束(例如,它们从一个给定的基类继承或它们实现了指定的接口)。在下面的例子中,我们实现了一个简单的可排序的单链表。链表由多个Node组成,每个Node必须保证添加进去的元素实现了IComparer接口。您可以这样声明:

public class Node<T> : IComparable<Node<T>> where T : IComparable<T>

这句代码定义了一个泛型Node,它操作类型TNode中的T实现了IComparable<T>接口,这意味着两个NodeT可以进行对比。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);
        }
    }
}

 

运行结果:

Adding2 1 2 6 1 5 9 0 5

Retrieving collections…

Integers0,1,1,1,2,2,5,5,6,9

EmployeesGeorge, John, Paul, Ringo

本例的开头声明了一个可以放到链表中的类:

public class Employee : IComparable<Employee>

这个声明指出Employee对象是可以进行对比的,可以看到Employee类实现了CompareToEquals方法。注意:这些方法是类型安全的(从参数传递进去的类是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 接口的数据。

你可能感兴趣的:(泛型)