zoukankan      html  css  js  c++  java
  • C#2所搭建的核心基础(一)泛型<一>

      以下几节中我将对C#2中增加的最重要的特性进行介绍。

      1)泛型---作为C#2最重要的新特性(同时也是.NET2.0的CLR中最重要的新特性),泛型实现了类型和方法的参数化。

      2)可空类型---值类型没有“值不存在”的概念。有了可空类型之后,就可以表示“缺少一个有意义的值”。

      3)委托---虽然委托在CLR的级别上没有任何变化,但C#2使它们使用起来更容易。除了语法得到了一些简化,匿名方法的引入,还引导我们采取更“函数化”的编程风格---这个趋势在C#3中得到了延续。

      4)迭代器---虽然一直以来,都可以利用C#的foreach语句来简单地使用迭代器,但C#1中,它实现起来却是一件让人痛苦的事情。C#2编译器能在幕后帮你构建一个状态机,从而隐藏了大量复杂性。

      对大多数人来说,泛型将成为C#2最重要的新特性。他们增强了性能,使代码更富有表现力,而且将大量的安全检查从执行时转移到了编译时进行。从根本上说,泛型实现了类型和方法的“参数化”,就像在普通方法调用中,经常要用参数来告诉他们使用什么值。同样,泛型的类型和方法也可以让参数告诉他们使用什么类型。

      搞懂泛型的方方面面不是一件容易的事情,这样我还是通过简单的例子来学习泛型。先来看看.NET2.0提供的一个集合类:Dictionary<TKey, TValue>。

    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    
    class 泛型字典
    {
        static Dictionary<string, int> CountWords(string text)
        {
            //创建单词到频率的新映射,它将有效统计每个单词在一段给定文本中出现的频率
            Dictionary<string, int> frequencies;
            frequencies = new Dictionary<string, int>();
            /*将文本分解成单词,对于每个单词,都检查它是否已经存在映射中,
            如果是则增加现有计数;否则就为单词赋予一个初始计数1 */
            string[] words = Regex.Split(text, @"\W+");
            foreach (string word in words)
            {
                if (frequencies.ContainsKey(word))
                {
                    /*这里需要注意下:负责递增的代码不需要执行到int的强制类型转换,就可以执行加法运算:
                    取回的值在编译时已经知道是int类型。使计数递增的步骤实际是先对映射的索引器执行一次取值操作,
                    然后增加,然后对索引器执行赋值操作。*/
                    frequencies[word]++;
                }
                else
                {
                    frequencies[word] = 1;
                }
            }
            return frequencies;
        }
    
    
        public static void Main()
        {
            Console.Write("Please input the text:");
            string text = Console.ReadLine();
    
            Dictionary<string, int> frequencies = CountWords(text);
            //打印映射中的每个键/值对。
            foreach (KeyValuePair<string, int> entry in frequencies)
            {
                string word = entry.Key;
                int frequency = entry.Value;
                Console.WriteLine("{0}:{1}", word, frequency);
            }
    
             
            Console.Read();
        }
    }

    运行效果如下图:

      既然我们已经看了一个例子,首先让我们来看一下Dictionary<TKey, TValue>的真正含义,什么是TKey和TValue,而且为什么要用尖括号把他们封闭起来。有两种形式的泛型:泛型类型(包括类、接口、委托和结构)和泛型方法。两者都是表示API的基本方法(不管是指一个泛型方法还是一个完整的泛型类型)。

      类型参数是真实类型的占位符。在泛型声明中,类型参数要放在一对尖括号内,并且要以逗号分隔开。所以在Dictionary<TKey, TValue>中,类型参数是TKey和TValue。在使用泛型类型或方法时,需要使用真实的类型代替,这些真实的类型称为类型实参(type argument),在上述代码中类型实参是string(代替TKey)和int(代替TValue)。

      如果没有为任何类型参数提供类型实参,声明的就是一个未绑定泛型类型(unbound generic type)。如果指定了类型实参,该对象就成为一个已构造类型(constructed type)。我们知道,类型(无论是否是泛型)可以看做是对象的蓝图。同样,未绑定泛型类型是已构造类型的蓝图,它是一种额外的抽象层。关系如下图:

      更复杂的是,已构造类型可以是开放或封闭的。开放类型(open type)还包含了类型参数,而封闭类型(closed type)则不是开放的,类型的每个部分都是明确的。在C#代码中,唯一能看见未绑定泛型的地方(除了作为声明之外)就是在typeof操作符内。类型参数“接收”信息,类型实参“提供”信息。这个思路与方法参数/方法实参是一样的。只不过类型实参必须为类型,而不能为任意的值。只有在编译时才能知道类型实参的类型,它可以是(或包含)相关上下文中的类型参数。

      读者可以具体参考下Dictionary<TKey, TValue>的内容,有助于进一步理解,这里就不做啰嗦的说明了。

     

    泛型的发音 在向其他人描述泛型类型时,通常使用of来介绍类型参数或实参,因此List<T>读作list of T。当有多个类型参数时,可以用一个适合整个类型含义的单词来分隔他们,例如:我经常用dictionary of string to int 来强调映射的部分,而不会使用 tuple of string and int。

    类型参数命名规范 虽然可以使用带有T、U和V这样的类型参数的类型,但从中根本看不出实际指的是什么,也看不出他们应该如何使用。相比之下,像Dictionary<TKey, TValue>这样的命名就好的多,TKey明显表示keys的类型,而TValue代表values的类型。如果只有一个参数类型,而且它的含义很清楚,那么一般使用T(List<T>就是一个很好的例子)。如果有多个类型参数,则应根据含义来命名,并用T前缀来指明这是一个类型参数。

  • 相关阅读:
    Redundant Paths 分离的路径(边双连通分量)
    bzoj2208 [Jsoi2010] 连通数(tarjan点双连通分量 // dfs)
    [bzoj3331] [BeiJing2013] 压力(tarjan 点双连通分量)
    [ BZOJ1123 ] BLO(tarjan点双连通分量)
    bitset小总结
    牛客328B Rabbit的工作(1)
    # Codeforces Round #529(Div.3)个人题解
    HDU5957 Query on a graph(拓扑找环,BFS序,线段树更新,分类讨论)
    istringstream()函数的用法
    codeforces 1077F1
  • 原文地址:https://www.cnblogs.com/52net/p/2670253.html
Copyright © 2011-2022 走看看