返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >浅谈C#数组(二)
  • 791
分享到

浅谈C#数组(二)

2024-04-02 19:04:59 791人浏览 八月长安
摘要

目录一.枚举集合 1.IEnumerator接口 2.foreach语句 3.yield语句 二.元组(Tuple) 三.结构比较 可以先了解上一篇文章内容C#数组(一) 一.枚举集

可以先了解上一篇文章内容C#数组(一)

一.枚举集合

  在foreach语句中使用枚举,可以迭代集合中的元素,且无需知道集合中元素的个数。foreach语句使用一个枚举器。foreach会调用实现了IEnumerable接口的集合类中的GetEumerator()方法。GetEumerator()方法返回一个实现IEnumerator接口的对象枚举。foreach语句就可以使用IEnumerable接口迭代集合了。
  GetEumerator()方法在IEnumerable接口中定义。

1.IEnumerator接口

  foreach语句使用IEnumerator接口的方法和属性,迭代集合中所有元素。IEnumerator接口定义了Current属性,来返回光标所在的元素,该接口的MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法就返回false.
  这个接口的泛型版本IEnumerator<T>派生自接口IDisposable,因此定义了Dispose()方法,来清理枚举器占用的资源。

2.foreach语句

  C#中foreach语句不会解析为IL代码中的foreach语句。C#编译器会把foreach语句转换为IEnumerator接口的方法和属性。


  Person[] persons = {
    new Person { FirstName="Damon", LastName="Hill" },
    new Person { FirstName="Niki", LastName="Lauda" },
    new Person { FirstName="Ayrton", LastName="Senna" },
    new Person { FirstName="Graham", LastName="Hill" }
  };
  foreach (Person p in persons)
  {
    Console.WriteLine(p);
  }

  foreach语句会解析为下面的代码:


  IEnumerator<Person> enumerator = persons.GetEumerator();
  while(enumerator.MoveNext())
  {
    Person p = enumerator.Current;
    Console.WriteLine(p);
  }

3.yield语句

  在C#2.0之前,foreach语句可以轻松的迭代集合,但创建枚举器需要做大量的工作。C#2.0添加了yield语句,以便创建枚举器。
  yield return 语句返回集合的一个元素,并移动到下一个元素。yield break可停止迭代。

下面的例子实现返回两个字符串:


  public class HelloCollection
  {
    public IEnumerator<string> GetEnumerator()
    {
    yield return "Hello";
    yield return "World";
    }
  }


客户端代码:


  var helloCollection = new HelloCollection();
  foreach (string s in helloCollection)
  {
    Console.WriteLine(s);
  }

  包含yield语句的方法或属性也称为迭代块。迭代块必须声明为返回IEnumeratorIEnumerable接口,或者这些接口的泛型版本。这个块可以包含多条yield return语句或yield break语句,但不能包含return语句。

  使用迭代块,编译器会生成一个yield类型,其中包含一个状态机,如下面代码所示:
  yield类型实现IEnumeratorIDisposable接口的方法和属性。下面的例子可以把yield类型看作内部类Enumerator。外部类的GetEnumerator()方法实例化并返回一个新的yield类型。在yield类型中,变量state定义了迭代的当前位置,每次调用MoveNext()时,当前位置都会改变。MoveNext()封装了迭代块的代码,并设置了current变量的值,从而使Current属性根据位置返回一个对象。


  public class HelloCollection
  {
    public IEnumerator<string> GetEnumerator()
    {
      return new Enumerator(0);
    }

  public class Enumerator:IEnumerator<string>,IEnumerator,IDisposable
  {
    private int state;
    private string current;

    public Enumerator(int state)
    {
      this.state = state;
    }

    bool System.Collections.IEnumerator.MoveNext()
    {
      switch(state)
      {
        case 0:
          current="hello";
          state =1;
          return true;
        case 1:
          current="world";
          state =2;
          return true;
        case 2:
          break;
      }

      return false;
    }

    void System.Collection>IEnumerator.Reset()
    {
      throw new NotSupportedException();
    }

    string System.Collections.Generic.IEnumerator<string>.Current
    {
      get
      {
        return current;
      }
    }

    object System.Collections.IEnumerator.Current
    {
      get
      {
        return current;
      }
    }

    void IDisposable.Dispose()
    {}
  }
}

  yield语句会产生一个枚举器,而不仅仅生成一个包含的项的列表。这个枚举器通过foreach语句调用。从foreach中依次访问每一项,就会访问枚举器。这样就可以迭代大量的数据,而无需一次把所有的数据都读入内存。

(1).迭代集合的不同方式

    可以使用yield return语句,以不同方式迭代集合。
    类MusicTitles可以用默认方式通过GetEnumerator()方法迭代标题,该方法不必在代码中编写,也可以用Reverse()逆序迭代标题,Subset()方法迭代子集合:


   public class MusicTitles
    {
      string[] names = {
      "Tubular Bells", "Hergest Ridge",
      "Ommadawn", "Platinum" };

      public IEnumerator<string> GetEnumerator()
      {
        for (int i = 0; i < 4; i++)
        {
          yield return names[i];
        }
      }

      public IEnumerable<string> Reverse()
      {
        for (int i = 3; i >= 0; i--)
        {
          yield return names[i];
        }
      }

      public IEnumerable<string> Subset(int index, int length)
      {
        for (int i = index; i < index + length;i++)
        {
          yield return names[i];
        }
      }
    }

客户端代码:


    var titles = new MusicTitles();
    foreach (var title in titles)
    {
      Console.WriteLine(title);
    }
    Console.WriteLine();

    Console.WriteLine("reverse");
    foreach (var title in titles.Reverse())
    {
      Console.WriteLine(title);
    }
    Console.WriteLine();

    Console.WriteLine("subset");
    foreach (var title in titles.Subset(2, 2))
    {
      Console.WriteLine(title);
    }

(2).用yield return 返回枚举器      


public class GameMoves
  {
    private IEnumerator cross;
    private IEnumerator circle;

    public GameMoves()
    {
      cross = Cross();
      circle = Circle();
    }

    private int move = 0;
    const int MaxMoves = 9;

    public IEnumerator Cross()
    {
      while (true)
      {
        Console.WriteLine("Cross, move {0}", move);
        if (++move >= MaxMoves)
          yield break;
        yield return circle;
      }
    }

    public IEnumerator Circle()
    {
      while (true)
      {
        Console.WriteLine("Circle, move {0}", move);
        if (++move >= MaxMoves)
          yield break;
        yield return cross;
      }
    }
  }

 

客户端代码:


   var game = new GameMoves();

    IEnumerator enumerator = game.Cross();
    while (enumerator.MoveNext())
    {
      enumerator = enumerator.Current as IEnumerator;
    }

这样会交替调用Cross()Circle()方法。

二.元组(Tuple)

  元组可以合并不同类型的对象。元组起源于函数编程语言,如F#。在.net Framework中,元组可用于所有的.Net语言。
  .NET Framework定义了8个泛型Tuple类和一个静态Tuple类,它们用作元组的工厂。不同的泛型Tuple类支持不同数量的元素。如,Tuple<T1>包含一个元素,Tuple<T1,T2>包含两个元素。

  Tuple<string, string> name = new Tuple<string, string>("Jochen", "Rindt");

元组也可以用静态Tuple类的静态Create()方法创建。Create()方法的泛型参数定了要实例化的元组类型:


  public static Tuple<int, int> Divide(int dividend, int divisor)
  {
    int result = dividend / divisor;
    int reminder = dividend % divisor;

    return Tuple.Create<int, int>(result, reminder);
  }

可以用属性Item1和Item2访问元组的项:


  var result = Divide(5, 2);
  Console.WriteLine("result of division: {0}, reminder: {1}", result.Item1, result.Item2);

  如果元组包含的项超过8个,就可以使用带8个参数的Tuple类定义。最后一个模板参数是TRest,表示必须给它传递一个元组。这样,就可以创建带任意个参数的元组了。


  var tuple = Tuple.Create<string, string, string, int, int, int, double, Tuple<int, int>>(
  "Stephanie", "Alina", "Nagel", 2009, 6, 2, 1.37, Tuple.Create<int, int>(52, 3490));

三.结构比较

  数组和元组都实现接口IStructuralEquatable和IStructuralComparable。这两个接口不仅可以比较引用,还可以比较内容。这些接口都是显式实现的,所以在使用时需要把数组和元组强制转换为这个接口。
  IStructuralEquatable接口用于比较两个元组或数组是否有相同的内同,IStructuralComparable接口用于给元组或数组排序

IStructuralEquatable接口示例:

  编写实现IEquatable接口的Person类,IEquatable接口定义了一个强类型化的Equals()方法,比较FirstNameLastName的值:


   public class Person : IEquatable<Person>
  {
    public int Id { get; private set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public override string ToString()
    {
      return String.FORMat("{0}, {1} {2}", Id, FirstName, LastName);
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return base.Equals(obj);
        return Equals(obj as Person);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }

    #region IEquatable<Person> Members

    public bool Equals(Person other)
    {
      if (other == null)
        return base.Equals(other);

      return this.FirstName == other.FirstName && this.LastName == other.LastName;
    }

    #endregion
  }

创建两个包含相同内容的Person类型的数组:


  var janet = new Person { FirstName = "Janet", LastName = "Jackson" };
  Person[] persons1 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };
  Person[] persons2 = { new Person { FirstName = "Michael", LastName = "Jackson" }, janet };


由于两个变量引用两个不同数组,所以!=返回True:


  if (persons1 != persons2)
    Console.WriteLine("not the same reference");

  对于IStructuralEquatable接口定义的Equals方法,第一个参数是object类型,第二个参数是IEqualityComparer类型。调用这个方法时,通过传递一个实现了EqualityComparer<T>的对象,就可以定义如何进行比较。通过EqualityComparer<T>类完成IEqualityComparer的一个默认实现。这个实现检查T类型是否实现了IEquatable接口,并调用IEquatable.Equals()方法。如果该类没有实现IEquatable接口,就调用Object基类中Equals()方法:


if ((persons1 as IStructuralEquatable).Equals(persons2, EqualityComparer<Person>.Default))
  {
    Console.WriteLine("the same content");
  }

元组示例:

Tuple<>类提供了两个Epuals()方法:一个重写了Object基类中的Epuals方法,并把object作为参数,第二个由IStructuralEquatable接口定义,并把object和IEqualityComparer作为参数。


  var t1 = Tuple.Create<int, string>(1, "Stephanie");
  var t2 = Tuple.Create<int, string>(1, "Stephanie");
  if (t1 != t2)
  Console.WriteLine("not the same reference to the tuple");

  这个方法使用EqualityComparer<object>.Default获取一个ObjectEqualityComparer<object>,以进行比较。这样就会调用Object.Equals()方法比较元组的每一项:


  if (t1.Equals(t2))
    Console.WriteLine("equals returns true");

还可以使用TupleComparer类创建一个自定义的IEqualityComparer


 TupleComparer tc = new TupleComparer();

  if ((t1 as IStructuralEquatable).Equals(t2, tc))
  {
    Console.WriteLine("yes, using TubpleComparer");
  }


  class TupleComparer : IEqualityComparer
  {
    #region IEqualityComparer Members

    public new bool Equals(object x, object y)
    {
      bool result = x.Equals(y);
      return result;
    }

    public int GetHashCode(object obj)
    {
      return obj.GetHashCode();
    }

    #endregion
  }

到此这篇关于浅谈C#数组的文章就介绍到这了,更多相关C#数组内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 浅谈C#数组(二)

本文链接: https://lsjlt.com/news/136427.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 浅谈C#数组(二)
    目录一.枚举集合 1.IEnumerator接口 2.foreach语句 3.yield语句 二.元组(Tuple) 三.结构比较 可以先了解上一篇文章内容C#数组(一) 一.枚举集...
    99+
    2024-04-02
  • 浅谈C#数组(一)
    目录一.简单数组之一维数组   1.数组的声明   2.数组的初始化3.访问数组元素4.数组中使用引用类型二.多维数组三.锯齿数组四.Array类1.创建数组2.复制数组3.排序五....
    99+
    2024-04-02
  • 浅谈JavaScript数组简介
    目录数组简介数组字面量二维数组总结数组简介 数组(Array) - 数组也是一个对象 它和我们普通对象功能类似,也是用来储存一些值的 不同的是普通对象是使用字符串作为属性值的,而...
    99+
    2024-04-02
  • 浅谈Python之Django(二)
    目录django设计数据表生成与修改各APP models设计users models 设计course models 设计organization models 设计operati...
    99+
    2024-04-02
  • 浅谈C++标准库
    目录C++模板 C++标准库 C++ 面向对象类库 string 标准模板库 vector setlist map queue priority_queue stack pair a...
    99+
    2024-04-02
  • 浅谈C#索引器
    目录一、概要二、应用场景一、概要 索引器使你可从语法上方便地创建类、结构或接口,以便客户端应用程序可以像访问数组一样访问它们。编译器将生成一个 Item 属性(或者如果存在 Inde...
    99+
    2024-04-02
  • 浅谈JS的二进制家族
    目录概述BlobBlob实战Blob下载文件Blob图片本地显示Blob文件分片上传本地读取文件内容ArrayBuffer通过ArrayBuffer的格式读取本地数据通过ArrayB...
    99+
    2024-04-02
  • 浅谈shell数组的定义及循环
    shell中数组的定义及遍历,先直接看示例: #!/bin/sh #定义方法一 数组定义为空格分割 arrayWen=(a b c d e f) #定义方法二 arrayXue[0]="m" arrayXue[1]=...
    99+
    2022-06-04
    shell 定义 循环
  • 浅谈Go数组比切片好在哪
    目录数组是什么切片是什么数组的优势可比较编译安全长度是类型规划内存布局访问速度总结参考前段时间有播放一条快讯,就是 Go1.17 会正式支持切片(Slice)转换到数据(Array)...
    99+
    2024-04-02
  • 浅谈C语言数组元素下标为何从0开始
        很多同学可能在学习数组时会有这个疑问,下标为什么不从1开始呢?从1开始不是更符合大家的日常习惯吗?生活中我们通常说第1个,而不是第0个。的确,有些计算机语...
    99+
    2024-04-02
  • 浅谈MySQL函数
    目录1、数学函数2、字符串函数3、日期函数4、加密函数 主要MySQL函数介绍又以下: 数学函数 字符串函数 时间函数 加密函数 ...
    99+
    2024-04-02
  • 浅谈Python numpy创建空数组的问题
    目录一、问题描述:二、具体的实现:三、完整代码:一、问题描述: 有一个shape为(308, 2)的二维数组,以及单独的一个数字,需要保存到csv文件中,这个单独的数字让其保存到第3列第一行的位置。 二、具体的实现...
    99+
    2022-06-02
    numpy创建空数组 python numpy
  • 浅谈shell 遍历数组的几种方法
    背景 某天遇到一个问题,现象如下:第一种遍历正常打印元素,然而第二种遍历只会打印一个元素! #!/bin/bash # $1 代表文件目录 # 执行 eg: # sh your_shell.sh /user/li...
    99+
    2022-06-04
    shell 遍历数组
  • 浅谈C语言结构体
    目录前言什么是结构体结构体类型的声明结构的自引用结构体变量的定义和初始化结构体的使用结构体内存对齐结构体传参总结前言 在C语言中,除了内置的许多数据类型,C语言还为我们提供了自定义的...
    99+
    2024-04-02
  • 浅谈C++有理数的表达和计算
    #ifndef Rational_hpp #define Rational_hpp #include <stdio.h> #include <string>...
    99+
    2024-04-02
  • 浅谈PostgreSQL数据库
    近期工作重点一直放在数据库设计方面,借助这次机会抽时间整理了一些我对PostgreSQL数据库的理解,同时也是对近段时间学习的一个总结。对于很多人而言或许没有听说过这个数据库,通常我们耳边伴随的都是Orac...
    99+
    2024-04-02
  • 浅谈一下Java线程组ThreadGroup
    目录1 简介2 线程组树的结构3 线程组的构造4 API5 终止线程组中的所有线程1 简介 一个线程集合。是为了更方便地管理线程。父子结构的,一个线程组可以集成其他线程组,同时也可以...
    99+
    2023-05-19
    Java 线程组 Java ThreadGroup
  • 浅谈VUE uni-app 基础组件
    1 .scroll-view 使用竖向滚动时,需要给 一个固定高度,通过 css 设置 height;使用横向滚动时,需要给添加white-space: nowrap;样式。 s...
    99+
    2024-04-02
  • 浅谈c++11闭包的实现
    目录什么是闭包仿函数:重载 operator()std::bind绑定器std::bindstd::bind和std::function配合使用什么是闭包 一个函数,带上了一个状态,...
    99+
    2024-04-02
  • 浅谈C++空间配置器allocator
    目录概述1. Allocator 的标准接口2. SGI STL 内存分配失败的异常处理3. SGI STL 内置轻量级内存池的实现4. SGI STL 内存池在多线程下的互斥访问概...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作