您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

2026-03-31 09:061阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计4160个文字,预计阅读时间需要17分钟。

您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

目录

一、集合

1.使用集合

2.自定义集合

3.索引符号

4.关键字值集合

5.迭代器

6.迭代器和集合

7.深度复制

二、比较

1.类型比较

2.值比较

3.运算符

目录
  • 一、集合
    • 1、使用集合
    • 2、自定义集合
    • 3、索引符
    • 4、关键字值集合和IDictionary
    • 5、迭代器
    • 6、迭代器和集合
    • 7、深度复制
  • 二、比较
    • 1、类型比较
      • 封箱和拆箱
      • is运算符
    • 2、值比较
      • 运算符重载
      • IComparable和IComparer接口
  • 三、转换
    • 1、重载转换运算符
      • 2、as运算符

      一、集合

      C#中的数组是作为System.Array类的实例来执行的,它们是集合类中的一种。
      集合类一般用于处理对象列表,其功能是通过执行System.Collection中的接口实现的。

      集合的功能可以通过接口来实现,该接口可以使用基本基本集合类,也可以创建自定义的集合类。

      System.Collections 命名空间有很多接口提供了基本的集合功能:

      • IEnumerable:公开枚举数,该枚举数支持在非泛型集合上进行简单迭代
      • ICollection:定义所有非泛型集合的大小、枚举数和同步方法
      • IList:表示可按照索引单独访问的对象的非泛型集合
      • IDictionary:表示键/值对的非通用集合

      System.Array类继承了IList,ICollection和IEnumerable。但不支持IList的一些高级功能,而且是一个大小固定的项目列表。

      1、使用集合

      Systems.Collections中的一个类System.Collections.ArrayList,也执行IList,ICollection和IEnumerable接口,但与数组不同,它是大小可变的
      使用System.Array类的集合(数组),必须用固定的大小来初始化数组
      例如:

      Animal[] animalArray = new Animal[2];

      使用System.ArrayList类的集合,不需要初始化其大小
      例如:

      ArrayList animalArrayList = new ArrayList();

      这个类还有两个构造函数:
      1 把现有集合作为参数复制到新实例中
      2 用一个int参数设置集合的容量,不过实际内容超过容量时会自动增加
      初始化数组,需要给这个项目赋予初始化了的对象
      例如:

      Cow myCow1 = new Cow("Deirdre"); animalArray[0] = myCow1; animalArray[1] = new Chicken("Ken");

      可以用这两种方式初始化数组

      对于ArrayList集合,需要用Add()方法添加新项目
      例如:

      Cow myCow2 = new Cow("Hayley"); animalArrayList.Add(myCow2); animalArrayList.Add(new Chicken("Roy"));

      在添加万项目之后,就可以用与数组相同的语法重写他们
      例如:

      animalArrayList[1] = new Chicken("Roy2")

      Array数组和ArrayList集合都支持foreach结构来迭代
      例如:

      foreach (Animal myAnimal in animalArray) { } foreach (Animal myAnimal in animalArrayList) { }

      Array数组使用Length属性获取项目的个数
      例如:

      int animalCount = animalArray.Length;

      ArrayList集合使用Count属性获取项目的个数

      int animalCount2 = animalArrayList.Count;

      Array数组是强类型化的,可以直接使用数组的类型来存储项目
      即可以直接访问项目的属性和方法
      例如:
      对于类型是Animal的数组,Feed()是类Animal的方法

      您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

      animalArray[0].Feed();

      但对于类Animal派生类的方法,就不能直接调用,需要强制转换

      ((Chicken)animalArray[1]).LayEgg();

      ArrayList集合是System.Object对象的集合,通过多态性赋給Animal对象
      必须进行数据类型转换
      例如:

      ((Animal)animalArrayList[0]).Feed(); ((Chicken)animalArrayList[1]).LayEgg();

      使用Remove()和RemoveAt()方法删除项目
      Remove 从 ArrayList 中移除特定对象的第一个匹配项(参数为特定对象)
      RemoveAt 移除 ArrayList 的指定索引处的元素(参数为索引值)
      删除项目后,会使其他项目在数组中移动一个位置

      使用AddRange()和InsertRange()方法可以一次添加多个项目
      AddRange 将 ICollection 的元素添加到 ArrayList 的末尾
      InsertRange 将集合中的某个元素插入 ArrayList 的指定索引处。
      例如:

      animalArrayList.AddRange(animalArray);

      使用IndexOf()方法获取指定项目的索引值
      IndexOf 返回 ArrayList 或它的一部分中某个值的第一个匹配项的从零开始的索引。
      可以通过索引值直接访问选项
      例如:

      int iIndex = animalArrayList.IndexOf(myCow1); ((Animal)animalArrayList[iIndex]).Feed();

      2、自定义集合

      可以从一个类派生自定义的集合
      推荐使用System.Collections.CollectionBase类。CollectionBase类有接口IEnumerable,ICollection,和IList

      List属性可以通过Ilist接口访问项目,InnerList属性用于存储项目的ArrayList对象

      例如:

      public class Animals : CollectionBase { public void Add(Animal newAnimal) { List.Add(newAnimal); } public void Remove(Animal oldAnimal) { List.Remove(oldAnimal); } public Animals() { } }

      这个类用于生成Animal类型的集合,可以用foreach访问其成员:

      Animals animalCollection = new Animals(); animalCollection.Add(new Cow("Sarah")); foreach (Animal myAnimal in animalCollection) { }

      如果要以索引的方式访问项目,就需要使用索引符

      3、索引符

      索引符是一种特殊类型的属性,可以把它添加到类中,提供类似数组的访问。
      最常见的一个用法是对项目执行一个数字索引

      例如:
      在Animals集合中添加一个索引符

      public class Animals : CollectionBase { ... public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; } set { List[animalIndex] = value; } } }

      this关键字与方括号一起,方括号中是索引参数
      对List使用一个索引符,而且显示声明了类型,因为IList接口返回的是System.Object对象
      现在可以用索引的方式访问项目:

      animalCollection[0].Feed();

      4、关键字值集合和IDictionary

      集合还可以执行类似的IDictionary接口,通过关键字值进行索引
      使用基类DictionaryBase,它也执行IEnumerable和ICollection接口,提供了对任何集合都相同的集合处理功能

      例如:

      public class Animals : DictionaryBase { public void Add(string newID, Animal newAnimal) { Dictionary.Add(newID, newAnimal); } public void Remove(string animalID) { Dictionary.Remove(animalID); } public Animals() { } public Animal this[string animalID] { get { return (Animal)Dictionary[animalID]; } set { Dictionary[animalID] = value; } } }

      这样添加了Add()方法,Remove()方法和一个通过关键字访问项目的方法
      其中Dictionary是包含在DictionaryBase实例中的元素的列表

      DictionaryBase集合和CollectionBase集合在foreach的工作方式不同,
      DictionaryBase提供的是DictionaryEntry结构,需要通过Value成员获取对象本身
      例如:
      CollectionBase集合:

      foreach (Animal myAnimal in animalCollection) { myAnimal.Feed(); }

      DictionaryBase集合:

      foreach (DictionaryEntry myEntry in animalCollection) { ((Animal)myEntry.Value).Feed(); }

      5、迭代器

      通过IEnumerable接口,可以使用foreach循环获取对象
      foreach循环,迭代collectionObject的过程:
      1 调用Collection的GetEnumerator()方法返回一个IEnumerator引用
      该方法也可以通过IEnumerable接口的实现代码获得
      2 调用IEnumerator接口的MoveNext()方法,将枚举数推进到集合的下一个元素
      3 如果MoveNext()方法返回true,使用IEnumerator接口的Current属性获取对象的引用,用于foreach循环
      4 重复前两个步骤,直至MoveNext()返回false时,循环停止

      迭代器是一个按顺序提供要在foreach循环中使用的所有值的代码块
      一般这个代码块是一个方法,也可以使用属性访问器和其他代码块作为迭代器
      代码块的返回值可能是IEnumerable或IEnumerator接口类型:
      1 如果要迭代一个类,可使用方法IEnumerator(),其返回类型是IEnumerator
      2 如果要迭代一个类成员,则使用IEnumerable

      在迭代器块中,使用yield关键字选择要在foreach循环中使用的值
      语法:

      yield return value;

      例如:

      public static IEnumerable SimpleList() { yield return "string 1"; yield return "string 2"; yield return "string 3"; } public static void Main(string[] args) { foreach (string item in SimpleList()) Console.WriteLine(item); Console.ReadKey(); }

      这里SimpleList就是迭代器块,是一个方法,使用IEnumerable返回类型
      可以从yield语句中返回任意类型

      可以中断信息返回foreach循环过程
      语法:yield break;

      6、迭代器和集合

      迭代器可以用于迭代储存在目录类型的集合中的对象
      例如:

      public new IEnumerator GetEnumerator() { foreach (object animal in Dictionary.Values) yield return (Animal)animal; }

      迭代集合中的对象:

      foreach (Animal myAnimal in animalCollection) { }

      7、深度复制

      使用System.Object.MemberwiseClone()方法可以进行阴影复制
      对于值类型成员,没什么问题
      但对于引用类型成员,新对象和源对象的成员将指向同一个引用对象
      例如:

      public class Content { public int Val; } public class Cloner { public Content MyContent = new Content(); public Cloner(int newVal) { MyContent.Val = newVal; } public object GetCopy() { return MemberwiseClone(); } }

      执行:

      Cloner mySource = new Cloner(5); Cloner myTarget = (Cloner)mySource.GetCopy(); int iVal1 = myTarget.MyContent.Val; mySource.MyContent.Val = 2; int iVal2 = myTarget.MyContent.Val;

      结果:
      iVal1是5,iVal2是2

      但有时候需要的是分别引用各自的对象,使用深度复制就可以解决
      标准方式是添加一个ICloneable接口,该接口有一个Clone()方法
      该方法不带参数,返回一个对象类型
      例如:

      public class Content { public int Val; } public class Cloner : ICloneable { public Content MyContent = new Content(); public int iVal = 0; public Cloner(int newVal) { MyContent.Val = newVal; } public object Clone() { Cloner clonedCloner = new Cloner(MyContent.Val); clonedCloner.iVal = iVal; return clonedCloner; } }

      通过Cloner对象的Val字段创建一个相同的Cloner对象
      如果有值成员需要复制,那必须给新对象添加上这个成员
      这样就能复制一个与源对象相同而互相独立的新对象

      用GetCopy换成Clone,执行相同的程序,结果:
      iVal1是5,iVal2是5

      二、比较

      1、类型比较

      比较对象时,需要先知道对象的类型,可以使用GetType()方法
      配合typeof()运算符一起使用,就可以确定对象的类型

      if (myObj.GetType() == typeof(MyComplexClass)) { // myObj is an instance of the class MyComplexClass. }

      封箱和拆箱

      处理值类型时后台的操作:
      封箱(boxing)是把值类型转换为System.Object类型或由值类型实现的接口类型
      拆箱(unboxing)是相反的过程
      例如:
      结构类型:

      struct MyStruct { public int Val; }

      把类型结构放在object类型变量中封箱:

      MyStruct valType1 = new MyStruct(); valType1.Val = 5; object refType = valType1;

      这里创建了一个MyStruct类型的valType1,给成员赋值后封箱到对象refType中
      这种方式的封装,将包含值类型的一个副本的引用,而不是源值的引用
      验证:

      valType1.Val = 6; MyStruct valType2 = (MyStruct)refType;

      结果valType2.Val是5而不是6

      如果对个引用类型进行封装,将包含源值的引用
      例如:

      class MyStruct { public int Val; }

      执行相同的操作,得到valType2.Val的值是6

      可以把值类型封箱到一个接口类型中:

      interface IMyInterface { } struct MyStruct : IMyInterface { public int Val; }

      把结构封箱到IMyInterface类型中:

      MyStruct valType1 = new MyStruct(); IMyInterface refType = valType1;

      拆箱:

      MyStruct ValType2 = (MyStruct)refType;

      封箱是在没有用户干涉的情况下进行的
      拆箱一个值需要进行显式转换(封箱是隐式的转换)
      访问值类型的内容前,必须进行拆箱

      is运算符

      is运算符可以检查对象是否是给定的类型,或者是否可以转换为给定的类型
      语法:

      is

      1 如果是一个类类型,也是该类型,或继承了该类型,或封箱到该类型中,则返回true
      2 如果是一个接口类型,也是该类型,或是实现该接口的类型,则返回true
      3 如果是一个值类型,也是该类型,或封箱到该类型中,则返回true

      2、值比较

      运算符重载

      要重载运算符,可给类添加运算符类型成员(必须是static)
      例如:
      重载+运算符:

      public class AddClass1 { public int val; public static AddClass3 operator +(AddClass1 op1, AddClass2 op2) { AddClass3 returnVal = new AddClass3(); returnVal.val = op1.val + op2.val; return returnVal; } } public class AddClass2 { public int val; } public class AddClass3 { public int val; }

      运算符重载与标准静态方法声明类似,但它使用关键字operator和运算符本身
      使用:

      AddClass1 op1 = new AddClass1(); op1.val = 5; AddClass2 op2 = new AddClass2(); op2.val = 4; AddClass3 op3 = op1 + op2;

      结果op3.val的值是9

      注意:如果混合了类型,操作数数序必须与运算符重载参数顺序相同

      可以重载的运算符:
      一元运算符:+,-,!,~,++,--,true,false
      二元运算符:+,-,*,/,%,&,|,^,<<,>>
      比较运算符:==,!=,<,>,<=,>=
      注意:
      如果重载true或false运算符,可以在布尔表达式中使用类,例如if (op1) {}

      运算符如 < 和 > 必须成对重载,但可以使用其他运算符来减少代码
      例如:

      class Addclass1 { public int val; public static bool operator >=(Addclass1 op1, Addclass2 op2) { return op1.val >= op2.val; } public static bool operator <(Addclass1 op1, Addclass2 op2) { return !(op1>= op2); } public static bool operator <=(Addclass1 op1, Addclass2 op2) { return op1.val <= op2.val; } public static bool operator >(Addclass1 op1, Addclass2 op2) { return !(op1 <= op2); } }

      同时适用于==和!=,常常需要重写Object.Equals()和Object.GetHashCode()

      class Addclass1 { public int val; public static bool operator ==(Addclass1 op1, Addclass1 op2) { return (op1.val == op2.val); } public static bool operator !=(Addclass1 op1, Addclass1 op2) { return !(op1== op2); } public override bool Equals(object obj) { return val == ((Addclass1)obj).val; } public override int GetHashCode() { return val; } }

      IComparable和IComparer接口

      IComparable和IComparer接口是比较对象的标准方式
      区别:
      IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象
      IComparer在一个单独的类中实现,可以比较任意两个对象

      .Net Framework 在类Comparer上提供了IComparer接口的默认实现方式,类Comparer位于System.Collections命名空间中,可以对简单类型以及支持IComparable接口的任意类型进行特定文化的比较

      public class SamplesComparer { public static void Main() { String str1 = "llegar"; String str2 = "lugar"; Console.WriteLine("Comparing \"{0}\" and \"{1}\" ", str1, str2); // Uses the DefaultInvariant Comparer. Console.WriteLine(" Invariant Comparer: {0}", Comparer.DefaultInvariant.Compare(str1, str2)); // Uses the Comparer based on the culture "es-ES" (Spanish - Spain, international sort). Comparer myCompIntl = new Comparer(new CultureInfo("es-ES", false)); Console.WriteLine(" International Sort: {0}", myCompIntl.Compare(str1, str2)) } }

      一般使用IComparable给出类的默认比较代码,使用其他类给出非默认的比较代码
      IComparable提供一个CompareTo()方法比较两个对象,并返回一个int值
      例如:

      class Person : IComparable { public string Name; public int Age; public Person(string name, int age) { Name = name; Age = age; } public int CompareTo(object obj) { if (obj is Person) { Person otherPerson = obj as Person; return this.Age - otherPerson.Age; } else { throw new ArgumentException( "Object to compare to is not a Person object."); } } }

      主程序代码

      class Program { static void Main(string[] args) { Person person1 = new Person("Jim", 30); Person person2 = new Person("Bob", 25); if (person1.CompareTo(person2) == 0) { Console.WriteLine("Same age"); } else if (person1.CompareTo(person2) > 0) { Console.WriteLine("person 1 is Older"); } else { Console.WriteLine("person1 is Younger"); } } }

      IComparer提供一个Compare()方法,接受两个对象返回一个整型结果
      例如:

      public class PersonComparer : IComparer { public static IComparer Default = new PersonComparer(); public int Compare(object x, object y) { if (x is Person && y is Person) { return Comparer.Default.Compare(((Person)x).Age, ((Person)y).Age); } else { throw new ArgumentException( "One or both objects to compare are not Person objects."); } } }

      主程序:

      class Program { static void Main(string[] args) { Person person1 = new Person("Jim", 30); Person person2 = new Person("Bob", 25); if (PersonComparer.Default.Compare(person1, person2) == 0) { Console.WriteLine("Same age"); } else if (PersonComparer.Default.Compare(person1, person2) > 0) { Console.WriteLine("person 1 is Older"); } else { Console.WriteLine("person1 is Younger"); } } }

      三、转换

      1、重载转换运算符

      implicit 关键字用于声明隐式的用户定义类型转换运算符
      explicit 关键字用于声明显式的用户定义类型转换运算符

      例如:

      public class ConvClass1 { public int val; public static implicit operator ConvClass2(ConvClass1 op1) { ConvClass2 returnVal = new ConvClass2(); returnVal.val = op1.val.ToString(); return returnVal; } } public class ConvClass2 { public string val; public static explicit operator ConvClass1(ConvClass2 op1) { ConvClass1 returnVal = new ConvClass1(); returnVal.val = Convert.ToInt32(op1.val); return returnVal; } }

      使用:

      ConvClass1 op1 = new ConvClass1(); op1.val = 5; ConvClass2 op2 = op1;

      这里使用了隐式转换,此时op2.val的值是字符"5"

      ConvClass2 op1 = new ConvClass2(); op1.val = "6"; ConvClass1 op2 = (ConvClass1)op1;

      这里使用了显示转换,此时op2.val的值是数字6

      2、as运算符

      as运算符可以把一种类型转换为指定的引用类型
      语法:

      as

      只适用于:
      1 的类型是类型
      2 可以隐式转换为类型
      3 可以封箱到类型中
      如果不能从转换为,则表达式结果是null

      例如:

      class ClassA : IMyInterface { } class ClassD : ClassA { } ClassA obj1 = new ClassA(); ClassD obj2 = obj1 as ClassD;

      obj2的结果是null

      使用一般的类型转换,出错时会抛出一个异常
      而as只会把null赋給对象,只要判断对象是否null就知道转换是否成功。

      到此这篇关于C#集合、比较和转换的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持自由互联。

      本文共计4160个文字,预计阅读时间需要17分钟。

      您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

      目录

      一、集合

      1.使用集合

      2.自定义集合

      3.索引符号

      4.关键字值集合

      5.迭代器

      6.迭代器和集合

      7.深度复制

      二、比较

      1.类型比较

      2.值比较

      3.运算符

      目录
      • 一、集合
        • 1、使用集合
        • 2、自定义集合
        • 3、索引符
        • 4、关键字值集合和IDictionary
        • 5、迭代器
        • 6、迭代器和集合
        • 7、深度复制
      • 二、比较
        • 1、类型比较
          • 封箱和拆箱
          • is运算符
        • 2、值比较
          • 运算符重载
          • IComparable和IComparer接口
      • 三、转换
        • 1、重载转换运算符
          • 2、as运算符

          一、集合

          C#中的数组是作为System.Array类的实例来执行的,它们是集合类中的一种。
          集合类一般用于处理对象列表,其功能是通过执行System.Collection中的接口实现的。

          集合的功能可以通过接口来实现,该接口可以使用基本基本集合类,也可以创建自定义的集合类。

          System.Collections 命名空间有很多接口提供了基本的集合功能:

          • IEnumerable:公开枚举数,该枚举数支持在非泛型集合上进行简单迭代
          • ICollection:定义所有非泛型集合的大小、枚举数和同步方法
          • IList:表示可按照索引单独访问的对象的非泛型集合
          • IDictionary:表示键/值对的非通用集合

          System.Array类继承了IList,ICollection和IEnumerable。但不支持IList的一些高级功能,而且是一个大小固定的项目列表。

          1、使用集合

          Systems.Collections中的一个类System.Collections.ArrayList,也执行IList,ICollection和IEnumerable接口,但与数组不同,它是大小可变的
          使用System.Array类的集合(数组),必须用固定的大小来初始化数组
          例如:

          Animal[] animalArray = new Animal[2];

          使用System.ArrayList类的集合,不需要初始化其大小
          例如:

          ArrayList animalArrayList = new ArrayList();

          这个类还有两个构造函数:
          1 把现有集合作为参数复制到新实例中
          2 用一个int参数设置集合的容量,不过实际内容超过容量时会自动增加
          初始化数组,需要给这个项目赋予初始化了的对象
          例如:

          Cow myCow1 = new Cow("Deirdre"); animalArray[0] = myCow1; animalArray[1] = new Chicken("Ken");

          可以用这两种方式初始化数组

          对于ArrayList集合,需要用Add()方法添加新项目
          例如:

          Cow myCow2 = new Cow("Hayley"); animalArrayList.Add(myCow2); animalArrayList.Add(new Chicken("Roy"));

          在添加万项目之后,就可以用与数组相同的语法重写他们
          例如:

          animalArrayList[1] = new Chicken("Roy2")

          Array数组和ArrayList集合都支持foreach结构来迭代
          例如:

          foreach (Animal myAnimal in animalArray) { } foreach (Animal myAnimal in animalArrayList) { }

          Array数组使用Length属性获取项目的个数
          例如:

          int animalCount = animalArray.Length;

          ArrayList集合使用Count属性获取项目的个数

          int animalCount2 = animalArrayList.Count;

          Array数组是强类型化的,可以直接使用数组的类型来存储项目
          即可以直接访问项目的属性和方法
          例如:
          对于类型是Animal的数组,Feed()是类Animal的方法

          您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

          animalArray[0].Feed();

          但对于类Animal派生类的方法,就不能直接调用,需要强制转换

          ((Chicken)animalArray[1]).LayEgg();

          ArrayList集合是System.Object对象的集合,通过多态性赋給Animal对象
          必须进行数据类型转换
          例如:

          ((Animal)animalArrayList[0]).Feed(); ((Chicken)animalArrayList[1]).LayEgg();

          使用Remove()和RemoveAt()方法删除项目
          Remove 从 ArrayList 中移除特定对象的第一个匹配项(参数为特定对象)
          RemoveAt 移除 ArrayList 的指定索引处的元素(参数为索引值)
          删除项目后,会使其他项目在数组中移动一个位置

          使用AddRange()和InsertRange()方法可以一次添加多个项目
          AddRange 将 ICollection 的元素添加到 ArrayList 的末尾
          InsertRange 将集合中的某个元素插入 ArrayList 的指定索引处。
          例如:

          animalArrayList.AddRange(animalArray);

          使用IndexOf()方法获取指定项目的索引值
          IndexOf 返回 ArrayList 或它的一部分中某个值的第一个匹配项的从零开始的索引。
          可以通过索引值直接访问选项
          例如:

          int iIndex = animalArrayList.IndexOf(myCow1); ((Animal)animalArrayList[iIndex]).Feed();

          2、自定义集合

          可以从一个类派生自定义的集合
          推荐使用System.Collections.CollectionBase类。CollectionBase类有接口IEnumerable,ICollection,和IList

          List属性可以通过Ilist接口访问项目,InnerList属性用于存储项目的ArrayList对象

          例如:

          public class Animals : CollectionBase { public void Add(Animal newAnimal) { List.Add(newAnimal); } public void Remove(Animal oldAnimal) { List.Remove(oldAnimal); } public Animals() { } }

          这个类用于生成Animal类型的集合,可以用foreach访问其成员:

          Animals animalCollection = new Animals(); animalCollection.Add(new Cow("Sarah")); foreach (Animal myAnimal in animalCollection) { }

          如果要以索引的方式访问项目,就需要使用索引符

          3、索引符

          索引符是一种特殊类型的属性,可以把它添加到类中,提供类似数组的访问。
          最常见的一个用法是对项目执行一个数字索引

          例如:
          在Animals集合中添加一个索引符

          public class Animals : CollectionBase { ... public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; } set { List[animalIndex] = value; } } }

          this关键字与方括号一起,方括号中是索引参数
          对List使用一个索引符,而且显示声明了类型,因为IList接口返回的是System.Object对象
          现在可以用索引的方式访问项目:

          animalCollection[0].Feed();

          4、关键字值集合和IDictionary

          集合还可以执行类似的IDictionary接口,通过关键字值进行索引
          使用基类DictionaryBase,它也执行IEnumerable和ICollection接口,提供了对任何集合都相同的集合处理功能

          例如:

          public class Animals : DictionaryBase { public void Add(string newID, Animal newAnimal) { Dictionary.Add(newID, newAnimal); } public void Remove(string animalID) { Dictionary.Remove(animalID); } public Animals() { } public Animal this[string animalID] { get { return (Animal)Dictionary[animalID]; } set { Dictionary[animalID] = value; } } }

          这样添加了Add()方法,Remove()方法和一个通过关键字访问项目的方法
          其中Dictionary是包含在DictionaryBase实例中的元素的列表

          DictionaryBase集合和CollectionBase集合在foreach的工作方式不同,
          DictionaryBase提供的是DictionaryEntry结构,需要通过Value成员获取对象本身
          例如:
          CollectionBase集合:

          foreach (Animal myAnimal in animalCollection) { myAnimal.Feed(); }

          DictionaryBase集合:

          foreach (DictionaryEntry myEntry in animalCollection) { ((Animal)myEntry.Value).Feed(); }

          5、迭代器

          通过IEnumerable接口,可以使用foreach循环获取对象
          foreach循环,迭代collectionObject的过程:
          1 调用Collection的GetEnumerator()方法返回一个IEnumerator引用
          该方法也可以通过IEnumerable接口的实现代码获得
          2 调用IEnumerator接口的MoveNext()方法,将枚举数推进到集合的下一个元素
          3 如果MoveNext()方法返回true,使用IEnumerator接口的Current属性获取对象的引用,用于foreach循环
          4 重复前两个步骤,直至MoveNext()返回false时,循环停止

          迭代器是一个按顺序提供要在foreach循环中使用的所有值的代码块
          一般这个代码块是一个方法,也可以使用属性访问器和其他代码块作为迭代器
          代码块的返回值可能是IEnumerable或IEnumerator接口类型:
          1 如果要迭代一个类,可使用方法IEnumerator(),其返回类型是IEnumerator
          2 如果要迭代一个类成员,则使用IEnumerable

          在迭代器块中,使用yield关键字选择要在foreach循环中使用的值
          语法:

          yield return value;

          例如:

          public static IEnumerable SimpleList() { yield return "string 1"; yield return "string 2"; yield return "string 3"; } public static void Main(string[] args) { foreach (string item in SimpleList()) Console.WriteLine(item); Console.ReadKey(); }

          这里SimpleList就是迭代器块,是一个方法,使用IEnumerable返回类型
          可以从yield语句中返回任意类型

          可以中断信息返回foreach循环过程
          语法:yield break;

          6、迭代器和集合

          迭代器可以用于迭代储存在目录类型的集合中的对象
          例如:

          public new IEnumerator GetEnumerator() { foreach (object animal in Dictionary.Values) yield return (Animal)animal; }

          迭代集合中的对象:

          foreach (Animal myAnimal in animalCollection) { }

          7、深度复制

          使用System.Object.MemberwiseClone()方法可以进行阴影复制
          对于值类型成员,没什么问题
          但对于引用类型成员,新对象和源对象的成员将指向同一个引用对象
          例如:

          public class Content { public int Val; } public class Cloner { public Content MyContent = new Content(); public Cloner(int newVal) { MyContent.Val = newVal; } public object GetCopy() { return MemberwiseClone(); } }

          执行:

          Cloner mySource = new Cloner(5); Cloner myTarget = (Cloner)mySource.GetCopy(); int iVal1 = myTarget.MyContent.Val; mySource.MyContent.Val = 2; int iVal2 = myTarget.MyContent.Val;

          结果:
          iVal1是5,iVal2是2

          但有时候需要的是分别引用各自的对象,使用深度复制就可以解决
          标准方式是添加一个ICloneable接口,该接口有一个Clone()方法
          该方法不带参数,返回一个对象类型
          例如:

          public class Content { public int Val; } public class Cloner : ICloneable { public Content MyContent = new Content(); public int iVal = 0; public Cloner(int newVal) { MyContent.Val = newVal; } public object Clone() { Cloner clonedCloner = new Cloner(MyContent.Val); clonedCloner.iVal = iVal; return clonedCloner; } }

          通过Cloner对象的Val字段创建一个相同的Cloner对象
          如果有值成员需要复制,那必须给新对象添加上这个成员
          这样就能复制一个与源对象相同而互相独立的新对象

          用GetCopy换成Clone,执行相同的程序,结果:
          iVal1是5,iVal2是5

          二、比较

          1、类型比较

          比较对象时,需要先知道对象的类型,可以使用GetType()方法
          配合typeof()运算符一起使用,就可以确定对象的类型

          if (myObj.GetType() == typeof(MyComplexClass)) { // myObj is an instance of the class MyComplexClass. }

          封箱和拆箱

          处理值类型时后台的操作:
          封箱(boxing)是把值类型转换为System.Object类型或由值类型实现的接口类型
          拆箱(unboxing)是相反的过程
          例如:
          结构类型:

          struct MyStruct { public int Val; }

          把类型结构放在object类型变量中封箱:

          MyStruct valType1 = new MyStruct(); valType1.Val = 5; object refType = valType1;

          这里创建了一个MyStruct类型的valType1,给成员赋值后封箱到对象refType中
          这种方式的封装,将包含值类型的一个副本的引用,而不是源值的引用
          验证:

          valType1.Val = 6; MyStruct valType2 = (MyStruct)refType;

          结果valType2.Val是5而不是6

          如果对个引用类型进行封装,将包含源值的引用
          例如:

          class MyStruct { public int Val; }

          执行相同的操作,得到valType2.Val的值是6

          可以把值类型封箱到一个接口类型中:

          interface IMyInterface { } struct MyStruct : IMyInterface { public int Val; }

          把结构封箱到IMyInterface类型中:

          MyStruct valType1 = new MyStruct(); IMyInterface refType = valType1;

          拆箱:

          MyStruct ValType2 = (MyStruct)refType;

          封箱是在没有用户干涉的情况下进行的
          拆箱一个值需要进行显式转换(封箱是隐式的转换)
          访问值类型的内容前,必须进行拆箱

          is运算符

          is运算符可以检查对象是否是给定的类型,或者是否可以转换为给定的类型
          语法:

          is

          1 如果是一个类类型,也是该类型,或继承了该类型,或封箱到该类型中,则返回true
          2 如果是一个接口类型,也是该类型,或是实现该接口的类型,则返回true
          3 如果是一个值类型,也是该类型,或封箱到该类型中,则返回true

          2、值比较

          运算符重载

          要重载运算符,可给类添加运算符类型成员(必须是static)
          例如:
          重载+运算符:

          public class AddClass1 { public int val; public static AddClass3 operator +(AddClass1 op1, AddClass2 op2) { AddClass3 returnVal = new AddClass3(); returnVal.val = op1.val + op2.val; return returnVal; } } public class AddClass2 { public int val; } public class AddClass3 { public int val; }

          运算符重载与标准静态方法声明类似,但它使用关键字operator和运算符本身
          使用:

          AddClass1 op1 = new AddClass1(); op1.val = 5; AddClass2 op2 = new AddClass2(); op2.val = 4; AddClass3 op3 = op1 + op2;

          结果op3.val的值是9

          注意:如果混合了类型,操作数数序必须与运算符重载参数顺序相同

          可以重载的运算符:
          一元运算符:+,-,!,~,++,--,true,false
          二元运算符:+,-,*,/,%,&,|,^,<<,>>
          比较运算符:==,!=,<,>,<=,>=
          注意:
          如果重载true或false运算符,可以在布尔表达式中使用类,例如if (op1) {}

          运算符如 < 和 > 必须成对重载,但可以使用其他运算符来减少代码
          例如:

          class Addclass1 { public int val; public static bool operator >=(Addclass1 op1, Addclass2 op2) { return op1.val >= op2.val; } public static bool operator <(Addclass1 op1, Addclass2 op2) { return !(op1>= op2); } public static bool operator <=(Addclass1 op1, Addclass2 op2) { return op1.val <= op2.val; } public static bool operator >(Addclass1 op1, Addclass2 op2) { return !(op1 <= op2); } }

          同时适用于==和!=,常常需要重写Object.Equals()和Object.GetHashCode()

          class Addclass1 { public int val; public static bool operator ==(Addclass1 op1, Addclass1 op2) { return (op1.val == op2.val); } public static bool operator !=(Addclass1 op1, Addclass1 op2) { return !(op1== op2); } public override bool Equals(object obj) { return val == ((Addclass1)obj).val; } public override int GetHashCode() { return val; } }

          IComparable和IComparer接口

          IComparable和IComparer接口是比较对象的标准方式
          区别:
          IComparable在要比较的对象的类中实现,可以比较该对象和另一个对象
          IComparer在一个单独的类中实现,可以比较任意两个对象

          .Net Framework 在类Comparer上提供了IComparer接口的默认实现方式,类Comparer位于System.Collections命名空间中,可以对简单类型以及支持IComparable接口的任意类型进行特定文化的比较

          public class SamplesComparer { public static void Main() { String str1 = "llegar"; String str2 = "lugar"; Console.WriteLine("Comparing \"{0}\" and \"{1}\" ", str1, str2); // Uses the DefaultInvariant Comparer. Console.WriteLine(" Invariant Comparer: {0}", Comparer.DefaultInvariant.Compare(str1, str2)); // Uses the Comparer based on the culture "es-ES" (Spanish - Spain, international sort). Comparer myCompIntl = new Comparer(new CultureInfo("es-ES", false)); Console.WriteLine(" International Sort: {0}", myCompIntl.Compare(str1, str2)) } }

          一般使用IComparable给出类的默认比较代码,使用其他类给出非默认的比较代码
          IComparable提供一个CompareTo()方法比较两个对象,并返回一个int值
          例如:

          class Person : IComparable { public string Name; public int Age; public Person(string name, int age) { Name = name; Age = age; } public int CompareTo(object obj) { if (obj is Person) { Person otherPerson = obj as Person; return this.Age - otherPerson.Age; } else { throw new ArgumentException( "Object to compare to is not a Person object."); } } }

          主程序代码

          class Program { static void Main(string[] args) { Person person1 = new Person("Jim", 30); Person person2 = new Person("Bob", 25); if (person1.CompareTo(person2) == 0) { Console.WriteLine("Same age"); } else if (person1.CompareTo(person2) > 0) { Console.WriteLine("person 1 is Older"); } else { Console.WriteLine("person1 is Younger"); } } }

          IComparer提供一个Compare()方法,接受两个对象返回一个整型结果
          例如:

          public class PersonComparer : IComparer { public static IComparer Default = new PersonComparer(); public int Compare(object x, object y) { if (x is Person && y is Person) { return Comparer.Default.Compare(((Person)x).Age, ((Person)y).Age); } else { throw new ArgumentException( "One or both objects to compare are not Person objects."); } } }

          主程序:

          class Program { static void Main(string[] args) { Person person1 = new Person("Jim", 30); Person person2 = new Person("Bob", 25); if (PersonComparer.Default.Compare(person1, person2) == 0) { Console.WriteLine("Same age"); } else if (PersonComparer.Default.Compare(person1, person2) > 0) { Console.WriteLine("person 1 is Older"); } else { Console.WriteLine("person1 is Younger"); } } }

          三、转换

          1、重载转换运算符

          implicit 关键字用于声明隐式的用户定义类型转换运算符
          explicit 关键字用于声明显式的用户定义类型转换运算符

          例如:

          public class ConvClass1 { public int val; public static implicit operator ConvClass2(ConvClass1 op1) { ConvClass2 returnVal = new ConvClass2(); returnVal.val = op1.val.ToString(); return returnVal; } } public class ConvClass2 { public string val; public static explicit operator ConvClass1(ConvClass2 op1) { ConvClass1 returnVal = new ConvClass1(); returnVal.val = Convert.ToInt32(op1.val); return returnVal; } }

          使用:

          ConvClass1 op1 = new ConvClass1(); op1.val = 5; ConvClass2 op2 = op1;

          这里使用了隐式转换,此时op2.val的值是字符"5"

          ConvClass2 op1 = new ConvClass2(); op1.val = "6"; ConvClass1 op2 = (ConvClass1)op1;

          这里使用了显示转换,此时op2.val的值是数字6

          2、as运算符

          as运算符可以把一种类型转换为指定的引用类型
          语法:

          as

          只适用于:
          1 的类型是类型
          2 可以隐式转换为类型
          3 可以封箱到类型中
          如果不能从转换为,则表达式结果是null

          例如:

          class ClassA : IMyInterface { } class ClassD : ClassA { } ClassA obj1 = new ClassA(); ClassD obj2 = obj1 as ClassD;

          obj2的结果是null

          使用一般的类型转换,出错时会抛出一个异常
          而as只会把null赋給对象,只要判断对象是否null就知道转换是否成功。

          到此这篇关于C#集合、比较和转换的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持自由互联。