C#入門學習之集合、比較和轉換
一、集合
C#中的數組是作為System.Array類的實例來執(zhí)行的,它們是集合類中的一種。
集合類一般用于處理對象列表,其功能是通過執(zhí)行System.Collection中的接口實現的。
集合的功能可以通過接口來實現,該接口可以使用基本基本集合類,也可以創(chuàng)建自定義的集合類。
System.Collections 命名空間有很多接口提供了基本的集合功能:
- IEnumerable:公開枚舉數,該枚舉數支持在非泛型集合上進行簡單迭代
- ICollection:定義所有非泛型集合的大小、枚舉數和同步方法
- IList:表示可按照索引單獨訪問的對象的非泛型集合
- IDictionary:表示鍵/值對的非通用集合
System.Array類繼承了IList,ICollection和IEnumerable。但不支持IList的一些高級功能,而且是一個大小固定的項目列表。
1、使用集合
Systems.Collections中的一個類System.Collections.ArrayList,也執(zhí)行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對象的集合,通過多態(tài)性賦給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、索引符
索引符是一種特殊類型的屬性,可以把它添加到類中,提供類似數組的訪問。
最常見的一個用法是對項目執(zhí)行一個數字索引
例如:
在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
集合還可以執(zhí)行類似的IDictionary接口,通過關鍵字值進行索引
使用基類DictionaryBase,它也執(zhí)行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循環(huán)獲取對象
foreach循環(huán),迭代collectionObject的過程:
1 調用Collection的GetEnumerator()方法返回一個IEnumerator引用
該方法也可以通過IEnumerable接口的實現代碼獲得
2 調用IEnumerator接口的MoveNext()方法,將枚舉數推進到集合的下一個元素
3 如果MoveNext()方法返回true,使用IEnumerator接口的Current屬性獲取對象的引用,用于foreach循環(huán)
4 重復前兩個步驟,直至MoveNext()返回false時,循環(huán)停止
迭代器是一個按順序提供要在foreach循環(huán)中使用的所有值的代碼塊
一般這個代碼塊是一個方法,也可以使用屬性訪問器和其他代碼塊作為迭代器
代碼塊的返回值可能是IEnumerable或IEnumerator接口類型:
1 如果要迭代一個類,可使用方法IEnumerator(),其返回類型是IEnumerator
2 如果要迭代一個類成員,則使用IEnumerable
在迭代器塊中,使用yield關鍵字選擇要在foreach循環(huán)中使用的值
語法:
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循環(huán)過程
語法: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(); } }
執(zhí)行:
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字段創(chuàng)建一個相同的Cloner對象
如果有值成員需要復制,那必須給新對象添加上這個成員
這樣就能復制一個與源對象相同而互相獨立的新對象
用GetCopy換成Clone,執(zhí)行相同的程序,結果:
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;
這里創(chuàng)建了一個MyStruct類型的valType1,給成員賦值后封箱到對象refType中
這種方式的封裝,將包含值類型的一個副本的引用,而不是源值的引用
驗證:
valType1.Val = 6; MyStruct valType2 = (MyStruct)refType;
結果valType2.Val是5而不是6
如果對個引用類型進行封裝,將包含源值的引用
例如:
class MyStruct { public int Val; }
執(zhí)行相同的操作,得到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; }
運算符重載與標準靜態(tài)方法聲明類似,但它使用關鍵字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接口是比較對象的標準方式
區(qū)別:
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#集合、比較和轉換的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。