接着上一篇《C#总结之面向对象编程》,继续总结C#基础语法,这篇主要总结集合,比较,转换,迭代器,对象拷贝,拆装箱,运算符重载,集合排序等基础语法及简单示例,方便以后查询。
采用继承System.Collections.CollectionBase的方法自定义合集。
public class Animals : CollectionBase { public void Add(Animal newAnimal) => List.Add(newAnimal); public void Remove(Animal newAnimal) => List.Remove(newAnimal); public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; } set { List[animalIndex] = value; } } } static void Main(string[] args) { Animals animalsCollection = new Animals(); animalsCollection.Add(new Cow("cow1")); animalsCollection.Add(new Chicken("Chicken1")); foreach(Animal myAnimal in animalsCollection) { myAnimal.Feed(); } ReadKey(); }迭代器是一个代码块,按顺序提供了要在foreach块中使用的所有值。如果要迭代一个类,则使用方法GetEnumerator(),其返回类型是IEnumberator。如要返回IEnumberator类型的值,则需使用yield return value的语句。示例代码:实现一个迭代器,获取素数。
public class Primes { private long min; private long max; public Primes() : this(2, 100) { } public Primes(long minimum,long maximum) { if (minimum < 2) { min = 2; } else { min = minimum; } max = maximum; } // 迭代器 public IEnumerator GetEnumerator() { for(long possiblePrime = min; possiblePrime <= max; possiblePrime++) { bool isPrime = true; for(long possibleFactor = 2; possibleFactor <= (long)Math.Floor(Math.Sqrt(possiblePrime)); possibleFactor++) { long remainderAfterDivision = possiblePrime % possibleFactor; if (remainderAfterDivision == 0) { isPrime = false; break; } } if (isPrime) { yield return possiblePrime; } } } } // 使用迭代器 static void Main(string[] args) { Primes primesFrom2To1000 = new Primes(2, 10000); foreach (long i in primesFrom2To1000) Write($"{i} "); ReadKey(); }实现ICloneable接口,调用Clone方法是一个递归的过程,涉及的引用类型,都要进行深度复制。
public class Content { public int Val; } public class Cloner:ICloneable { public Content MyContent = new Content(); public Cloner(int newVal) => MyContent.Val = newVal; public object Clone() { Cloner clonerObj = new Cloner(MyContent.Val); return clonerObj; } } static void Main(string[] args) { Cloner mySource = new Cloner(5); Cloner myTarget = (Cloner)mySource.Clone(); WriteLine($"myTarget.MuContent.Val={myTarget.MyContent.Val}"); mySource.MyContent.Val = 3; WriteLine($"myTarget.MuContent.Val={myTarget.MyContent.Val}"); ReadKey(); }对于值类型,执行浅度复制即可。
public class Cloner { public int Val; public Cloner(int newVal) => Val = newVal; public object GetCopy() => MemberwiseClone(); }封箱是把值类型转换为System.Object类型或者转换为由值类型实现的接口类型,拆箱是相反的过程。
struct MyStruct { public int Val; } static void Main(string[] args) { MyStruct valType = new MyStruct(); valType.Val = 5; // 封箱 object refType = valType; valType.Val = 6; //拆箱 MyStruct valType2=(MyStruct)refType; WriteLine($"valType2.Val={valType2.Val}"); ReadKey(); }也可以把值类型封装到接口类型中
interface IMyInterface { } struct MyStruct : IMyInterface { public int Val; } static void Main(string[] args) { MyStruct valType1 = new MyStruct(); IMyInterface refType = valType1; MyStruct valType2 = (MyStruct)refType; ReadKey(); }is运算符是用来检查对象是不是给定类型或者是否可以转换为给定类型。
interface IMyInterface { } class ClassA : IMyInterface { } class ClassB : IMyInterface { } class ClassC { } class ClassD : ClassA { }; struct MyStruct : IMyInterface { } class Checker { public static void Check(object param1) { if (param1 is ClassA) { WriteLine($"{param1} is ClassA"); }else { WriteLine($"{param1} is not ClassA"); } if(param1 is IMyInterface) { WriteLine($"{param1} is IMyInterface"); }else { WriteLine($"{param1} is not IMInterface"); } if(param1 is MyStruct) { WriteLine($"{param1} is MyStruct"); } else { WriteLine($"{param1} is not MyStruct"); } WriteLine(); } } // 使用 ClassA try1 = new ClassA(); ClassB try2 = new ClassB(); ClassC try3 = new ClassC(); ClassD try4 = new ClassD(); MyStruct try5 = new MyStruct(); object try6 = try5; Checker.Check(try1); Checker.Check(try2); Checker.Check(try3); Checker.Check(try4); Checker.Check(try5); Checker.Check(try6);还可以用is运算符做模式匹配
object[] data = {1.6180,null,new Cow("Rual"),new Chicken("Lea"),"none" }; foreach(var item in data) { if (item is 1.6180) WriteLine("The Golden Ratio"); else if (item is null) WriteLine("The value is null"); else if (item is Cow co) WriteLine($"The cow is named {co.Name}"); else if (item is Chicken ch) WriteLine($"The chicken is named {ch.Name}"); else if (item is var catcher) WriteLine($"Catch all for {catcher.GetType().Name}"); }通过运算符的重载,可以对我们设计的类使用标准的运算符。重载所有二元运算符都是一样的,一元运算符也是同样的,但只有一个参数。一些运算符如<和>必须成对重载。
public class AddClass1 { public int val; public static AddClass1 operator +(AddClass1 op1,AddClass1 op2) { AddClass1 returnVal = new AddClass1(); returnVal.val = op1.val + op2.val; return returnVal; } public static AddClass1 operator -(AddClass1 op1) { AddClass1 returnVal = new AddClass1(); returnVal.val = -op1.val; return returnVal; } public static bool operator >(AddClass1 op1, AddClass1 op2) => (op1.val > op2.val); public static bool operator <(AddClass1 op1, AddClass1 op2) => !(op1>op2); public override bool Equals(object op1) { if (op1.GetType() == typeof(AddClass1)) { return val == ((AddClass1)op1).val; } else { return false; } } public override int GetHashCode() => val; } // 使用 AddClass1 op1 = new AddClass1(); op1.val = 1; AddClass1 op2 = new AddClass1(); op2.val = 4; AddClass1 op3 = op1 + op2; WriteLine($"op3.val={op3.val}");IComparable和IComparer接口是比较对象的两种标准方式。
// IComparable接口可以比较对象和另一个对象 public 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"); } } } // 在一个单独的类中实现,可以比较任意两个对象。 public class PersonCompareName : IComparer { public static IComparer Default = new PersonCompareName(); public int Compare(object x, object y) { if(x is Person && y is Person) { return Comparer.Default.Compare(((Person)x).Name, ((Person)y).Name); } else { throw new ArgumentException("One or both objects to compare are not person objects"); } } } //使用 ArrayList listPerson = new ArrayList(); listPerson.Add(new Person("Rual", 16)); listPerson.Add(new Person("Donna", 28)); listPerson.Add(new Person("Mary", 19)); listPerson.Add(new Person("Ben", 20)); listPerson.Add(new Person("Dahlin", 8)); for(int i = 0; i < listPerson.Count; i++) { WriteLine($"{(listPerson[i] as Person).Name} - {(listPerson[i] as Person).Age}"); } WriteLine(); listPerson.Sort(); for (int i = 0; i < listPerson.Count; i++) { WriteLine($"{(listPerson[i] as Person).Name} - {(listPerson[i] as Person).Age}"); } WriteLine(); listPerson.Sort(PersonCompareName.Default); for (int i = 0; i < listPerson.Count; i++) { WriteLine($"{(listPerson[i] as Person).Name} - {(listPerson[i] as Person).Age}"); }如果要在不相关的类型之间转换,例如类型之间没有继承关系,也没有共享接口,就必须重载转换运算符。有几个注意事项:
explicit 和 implicit 属于转换运算符,如用这两者可以让我们自定义的类型支持相互交换explicti 表示显式转换,如从 A -> B 必须进行强制类型转换(B = (B)A)implicit 表示隐式转换,如从 B -> A 只需直接赋值(A = B)隐式转换可以让我们的代码看上去更漂亮、更简洁易懂,所以最好多使用 implicit 运算符。不过!如果对象本身在转换时会损失一些信息(如精度),那么我们只能使用 explicit 运算符,以便在编译期就能警告客户调用端。其中as 运算符可以把一种类型转换为指定的引用类型。
public class ConvClass1 { public int val; // 隐式转换 public static implicit operator ConvClass2(ConvClass1 op1) { ConvClass2 returnVal = new ConvClass2(); returnVal.val = op1.val; return returnVal; } } public class ConvClass2 { public double val; // 需要显示转换 public static explicit operator ConvClass1(ConvClass2 op1) { ConvClass1 returnVal = new ConvClass1(); // 超出赋值范围时,将产生一个异常。 checked { returnVal.val = (int)op1.val; }; return returnVal; } } // 使用 ConvClass1 op1 = new ConvClass1(); op1.val = 3; ConvClass2 op2 = op1; WriteLine(op2.val); ConvClass1 op3 = (ConvClass1)op2; WriteLine(op3.val);