目录
- 一、集合
- 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的方法
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#集合、比较和转换的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持自由互联。
