科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道C# 2.0版本 泛型简介(四)

C# 2.0版本 泛型简介(四)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

   泛型和 .NET Framework   为了对本文做一下小结,下面介绍 .NET 中除 C# 本身以外的其他一些领域如何利用泛型或者与泛型交互。

作者:中国IT实验室 来源:中国IT实验室 2007年9月12日

关键字: 编程

  • 评论
  • 分享微博
  • 分享邮件
泛型和 .NET Framework

  为了对本文做一下小结,下面介绍 .NET 中除 C# 本身以外的其他一些领域如何利用泛型或者与泛型交互。

  System.Array 和泛型

  System.Array 类型通过很多一般静态方法进行了扩展。这些一般静态方法专门用于自动执行和简化处理数组的常见任务,例如,遍历数组并且对每个元素执行操作、扫描数组,以查找匹配某个条件(谓词)的值、对数组进行变换和排序等等。代码块 11 是这些静态方法的部分清单。

  代码块 11. System.Array 的一般方法

public abstract class Array
{
   //Partial listing of the static methods:  
   public static IList AsReadOnly(T[] array);
   public static int BinarySearch(T[] array, T value);
   public static int BinarySearch(T[] array, T value,
                                     IComparer comparer);
   public static U[] ConvertAll(T[] array, 
                                Converter converter);
   public static bool Exists(T[] array,Predicate match);
   public static T Find(T[] array,Predicate match);
   public static T[] FindAll(T[] array, Predicate match);
   public static int FindIndex(T[] array, Predicate match);
   public static void ForEach(T[] array, Action action);
   public static int  IndexOf(T[] array, T value);
   public static void Sort(K[] keys, V[] items,
                                IComparer comparer);
   public static void Sort(T[] array,Comparison comparison) 
}

System.Array 的静态一般方法都使用 system 命名空间中定义的下列四个一般委托:

public delegate void Action(T t);
public delegate int Comparison(T x, T y);
public delegate U Converter(T from);
public delegate bool Predicate(T t);

  代码块 12 演示如何使用这些一般方法和委托。它用从 1 到 20 的所有整数初始化一个数组。然后,代码通过一个匿名方法和 action 委托,使用 array.foreach() 方法来跟踪这些数字。使用第二个匿名方法和 predicate 委托,代码通过调用 array.findall() 方法(它返回另一个相同的一般类型的数组),来查找该数组中的所有质数。最后,使用相同的 action 委托和匿名方法来跟踪这些质数。请注意代码块 12 中类型参数推理的用法。您在使用静态方法时无须指定类型参数。

代码块 12. 使用 System.Array 的一般方法

int[] numbers = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
Action trace =  delegate(int number)
                     {
                        Trace.WriteLine(number);
                     };
Predicate isPrime =   delegate(int number)
                           {
                              switch(number)
                              {
                                case 1:case 2:case 3:case 5:case 7:                                 
                                case 11:case 13:case 17:case 19:
                                    return true;
                                 default:
                                    return false;
                              }
                           };
Array.ForEach(numbers,trace);
int[] primes = Array.FindAll(numbers,isPrime);
Array.ForEach(primes,trace);

  在 system.collections.generic 命名空间中定义的类 list 中,也可以得到类似的一般方法。这些方法使用四个相同的一般委托。实际上,您还可以在您的代码中利用这些委托,如以下部分所示。

  静态集合类

  尽管 system.array 和 list 都提供了能够大大简化自身使用方式的、方便的实用工具方法,但 .NET 没有为其他集合提供这样的支持。为了对此进行补偿,本文随附的源代码包含了静态 Helper 类 collection,其定义如下所示:

public static class Collection
{
   public static IList AsReadOnly(IEnumerable collection);
   public static U[] ConvertAll(IEnumerable collection,
                                     Converter converter);
   public static bool Contains(IEnumerable collection,T item) 
                                               where T : IComparable;
   public static bool Exists(IEnumerable collection,Predicate); 
   public static T Find(IEnumerable collection,Predicate match);
   public static T[] FindAll(IEnumerable collection,
                                Predicate match);
   public static int FindIndex(IEnumerable collection,T value) 
                                                where T : IComparable;
   public static T FindLast(IEnumerable collection,
                               Predicate match);
   public static int FindLastIndex(IEnumerable collection,T value) 
                                                where T : IComparable;
   public static void ForEach(IEnumerable collection,Action action);
   public static T[] Reverse(IEnumerable collection);
   public static T[] Sort(IEnumerable collection);
   public static T[] ToArray(IEnumerable collection);
   public static bool TrueForAll(IEnumerable collection,
                                    Predicate match);
   //Overloaded versions for IEnumerator 
}

Collection 的实现简单易懂。例如,以下为 foreach() 方法:

public static void ForEach(IEnumerator iterator,Action action)
{
   /* Some parameter checking here, then: */
   while(iterator.MoveNext())
   {
      action(iterator.Current);
   }
}

Collection 静态类的用法非常类似于 array 和 list,它们都利用相同的一般委托。您可以将 collection 用于任何集合,只要该集合支持 ienumerable 或 ienumerator:

Queue queue = new Queue();
//Some code to initialize queue
Action trace =  delegate(int number)
                     {
                        Trace.WriteLine(number);
                     };
Collection.ForEach(queue,trace);

一般集合

  system.collections 中的数据结构全部都是基于 Object 的,因而继承了本文开头描述的两个问题,即性能较差和缺少类型安全。.NET 2.0 在 System.Collections.Generic 命名空间中引入了一组一般集合。例如,有一般的 stack 类和一般的 queue 类。dictionary 数据结构等效于非一般的 hashtable,并且还有一个有点像 sortedlist 的 sorteddictionary 类。类 list 类似于非一般的 arraylist。表 1 将 System.Collections.Generic 的主要类型映射到 System.Collections 中的那些主要类型。

表 1. 将 System.Collections.Generic 映射到 System.Collections
System.Collections.Generic System.Collections

Comparer

Comparer

Dictionary

HashTable

LinkedList

-

List

ArrayList

Queue

Queue

SortedDictionary

SortedList

Stack

Stack

ICollection

ICollection

IComparable

System.IComparable

IDictionary

IDictionary

IEnumerable

IEnumerable

IEnumerator

IEnumerator

IList

IList

system.collections.generic 中的所有一般集合还实现了一般的 ienumerable 接口,该接口的定义如下所示:

public interface IEnumerable
{
   IEnumerator GetEnumerator();
} 
public interface IEnumerator : IDisposable
{
   T Current{get;}
   bool MoveNext();
}
      

简单说来,IEnumerable 提供了对 ienumerator 迭代器接口的访问,该接口用于对集合进行抽象迭代。所有集合都在嵌套结构上实现了 ienumerable,其中,一般类型参数 T 是集合存储的类型。

特别有趣的是,词典集合定义它们的迭代器的方式。词典实际上是两个类型(而非一个类型)的一般参数(键和值)集合。system.collection.generic 提供了一个名为 KeyValuePair 的一般结构,其定义如下所示:

struct KeyValuePair
{
   public KeyValuePair(K key,V value);
   public K Key(get;set;)
   public V Value(get;set;)
}

  keyvaluepair 简单地存储一般键和一般值组成的对。该结构就是词典作为集合进行管理的类型,并且是它用于实现它的 ienumerable 的类型。dictionary 类将一般 KeyValuePair 结构指定为 ienumerable 和 icollection 的项参数:

public class Dictionary : IEnumerable<KEYVALUEPAIR>, 
                               ICollection<KEYVALUEPAIR>, 
                               //More interfaces
{...} 

  keyvaluepair 中使用的键和值类型参数当然是词典自己的一般键和值类型参数。您无疑可以在您自己的使用键和值对的一般数据结构中完成同样的工作。例如:

public class LinkedList : IEnumerable<KEYVALUEPAIR> where K : IComparable
{...} 

序列化和泛型

.net 允许您具有可序列化的一般类型:

[Serializable]
public class MyClass
{...}

  在序列化类型时,除了持久保持对象成员的状态以外,.net 还持久保持有关该对象及其类型的元数据。如果可序列化的类型是一般类型并且它包含绑定类型,则有关该一般类型的元数据还包含有关该绑定类型的类型信息。因此,一般类型的每个带有特定参数类型的变形都被视为唯一的类型。例如,您不能将对象类型 myclass 序列化(而只能将其反序列化)为 myclass 类型的对象。序列化一般类型的实例与序列化非一般类型没有什么不同。但是,在反序列化该类型时,您需要通过匹配的特定类型声明变量,并且在向下强制转换从 Deserialize 返回的 Object 时再次指定这些类型。代码块 13 显示了一般类型的序列化和反序列化。

代码块 13. 一般类型的客户端序列化

[Serializable]
public class MyClass
{...}
MyClass obj1 = new MyClass();

IFormatter formatter = new BinaryFormatter();?
Stream stream = new FileStream("obj.bin",FileMode.Create,FileAccess.ReadWrite); 
using(stream)
{
   formatter.Serialize(stream,obj1);
   stream.Seek(0,SeekOrigin.Begin);

   MyClass obj2; 
   obj2 = (MyClass)formatter.Deserialize(stream);
}

请注意,IFormatter 是基于对象的。您可以通过定义 iformatter 的一般版本进行补偿:

public interface IGenericFormatter
{
   T Deserialize(Stream serializationStream);
   void Serialize(Stream serializationStream,T graph);
}

您可以通过包含一个基于对象的格式化程序来实现 igenericformatter:

public class GenericFormatter : IGenericFormatter 
                                   where F : IFormatter,new()
{
   IFormatter m_Formatter = new F();
   public T Deserialize(Stream serializationStream)
   {
      return (T)m_Formatter.Deserialize(serializationStream);
   }
   public void Serialize(Stream serializationStream,T graph)
   {
      m_Formatter.Serialize(serializationStream,graph);
   }
}

请注意一般类型参数 F 上的两个约束的用法。尽管可以原样使用 genericformatter<f>:

   
using GenericBinaryFormatter = GenericFormatter;
using GenericSoapFormatter2  = GenericFormatter;

但是,您还可以将该格式化程序强类型化以使用:

public sealed class GenericBinaryFormatter : GenericFormatter
{}
public sealed class GenericSoapFormatter : GenericFormatter
{}

强类型化定义的优点是可以跨文件和程序集共享它,这与 using 别名相反。

代码块 14 与代码块 13 相同,唯一的不同之处在于它使用一般格式化程序:

代码块 14. 使用 IGenericFormatter

[Serializable]
public class MyClass
{...}
MyClass obj1 = new MyClass();

IGenericFormatter formatter = new GenericBinaryFormatter();?
Stream stream = new FileStream("obj.bin",FileMode.Create,FileAccess.ReadWrite); 
using(stream)
{
   formatter.Serialize(stream,obj1);
   stream.Seek(0,SeekOrigin.Begin);
   MyClass obj2; 
   obj2 = formatter.Deserialize(stream);
}

  泛型和远程处理

  可以定义和部署利用泛型的远程类,并且可以使用编程或管理配置。请考虑使用泛型并且派生自 marshalbyrefobject 的类 myserver。

public class MyServer : MarshalByRefObject
{...}

  只有当类型参数 t 是可封送的对象时,您才能通过远程处理访问该类。这意味着 t 是可序列化的类型或者派生自 marshalbyrefobject。您可以通过将 t 约束为派生自 marshalbyrefobject 来实施这一要求。

public class MyServer : MarshalByRefObject 
                           where T : MarshalByRefObject
{...}

  在使用管理类型注册时,您需要指定要取代一般类型参数而使用的确切类型实参。您必须以与语言无关的方式命名这些类型,并且提供完全限定命名空间。例如,假设类 myserver 在命名空间 remoteserver 中的程序集 serverassembly 中定义,并且您希望在客户端激活模式下将其与整型而不是一般类型参数 T 一起使用。在该情况下,配置文件中必需的客户端类型注册条目应该是:

<client url="...some url goes here..."> 
   <activated type="RemoteServer.MyServer<b>[[System.Int32]]</b>,ServerAssembly"/>
</client> 

  配置文件中的匹配主机端类型注册条目是:

<service> 
   <activated type="RemoteServer.MyServer<b>[[System.Int32]]</b>,ServerAssembly"/> 
</service> 

双方括号用来指定多个类型。例如:

LinkedList[[System.Int32],[System.String]]

  在使用编程配置时,您可以用类似于 C# 1.1 的方式配置激活模式和类型注册,不同之处在于,当定义远程对象的类型时,您必须提供类型实参而不是一般类型参数。例如,对于主机端激活模式和类型注册,您可以编写如下代码:

Type serverType = typeof(MyServer);
RemotingConfiguration.RegisterActivatedServiceType(serverType);

对于客户端类型激活模式和位置注册,具有以下代码:

Type serverType = typeof(MyServer);      
string url = ...; //some url initialization 
RemotingConfiguration.RegisterWellKnownClientType(serverType,url);

当实例化远程服务器时,只须提供类型参数,就好像您在使用本地一般类型一样:

MyServer obj;
obj = new MyServer();
//Use obj

  除了使用 new 以外,客户端还可以选择使用 activator 类的方法来连接到远程对象。在使用 activator.getobject() 时,您需要提供要使用的类型实参,并且在显式强制转换返回的 Object 时提供实参类型:

string url = ...; //some url initialization 
Type serverType = typeof(MyServer);
MyServer obj;
obj = (MyServer)Activator.GetObject(serverType,url);
//Use obj

您还可以将 activator.createinstance() 与一般类型一起使用:

Type serverType = typeof(MyServer);
MyServer obj;
obj = (MyServer)Activator.CreateInstance(serverType);
//Use obj

实际上,Activator 还提供 createinstance() 的一般版本,定义如下:

T CreateInstance<T>();

CreateInstance() 的使用方式类似于非一般方法,只是添加了类型安全的好处:

Type serverType = typeof(MyServer);
MyServer obj;
obj = Activator.CreateInstance(serverType);
//Use obj

  泛型无法完成的工作

  在 .NET 2.0 下,您不能定义一般 Web 服务,即使用一般类型参数的 Web 方法。原因是没有哪个 Web 服务标准支持一般服务。

  您还不能在服务组件上使用一般类型。原因是泛型不能满足 COM 可见性要求,而该要求对于服务组件而言是必需的(就像您无法在 COM 或 COM+ 中使用 C++ 模板一样)。

小结

  c# 泛型是开发工具库中的一个无价之宝。它们可以提高性能、类型安全和质量,减少重复性的编程任务,简化总体编程模型,而这一切都是通过优雅的、可读性强的语法完成的。尽管 C# 泛型的根基是 C++ 模板,但 C# 通过提供编译时安全和支持将泛型提高到了一个新水平。C# 利用了两阶段编译、元数据以及诸如约束和一般方法之类的创新性的概念。毫无疑问,C# 的将来版本将继续发展泛型,以便添加新的功能,并且将泛型扩展到诸如数据访问或本地化之类的其他 .NET Framework 领域。

  Juval Lowy 是 IDesign(一家咨询和培训公司)的软件架构师和负责人。Juval 是 Microsoft 的 Silicon Valley 地区主管,他与 Microsoft 一起来帮助整个行业采用 .NET。他的最新著作是 programming .NET Components 2nd Edition (O'Reilly, 2005)。该书专门论述面向组件的编程和设计以及相关的系统问题。Juval 参加了 Microsoft 对 .NET 的将来版本进行的内部设计审查,并且频繁主持开发会议。Microsoft 将 Juval 视为软件传奇人物以及全球顶尖 .NET 专家与行业领导人之一。您可以通过 http://www.idesign.net 与 Juval 联系。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章