C#总结之集合,比较,转换

本文深入介绍了C#中的集合使用,包括数组、ArrayList和自定义集合Animals,以及键值对集合DictionaryBase的实现。此外,详细讨论了对象的比较,如装箱拆箱、is运算符、运算符重载,并展示了对象集合的排序。最后,阐述了迭代器的原理和应用,以及如何实现深度复制。
摘要由CSDN通过智能技术生成

C#集合,比较,转换

接着上一篇《C#总结之面向对象编程》,继续总结C#基础语法,这篇主要总结集合,比较,转换,迭代器,对象拷贝,拆装箱,运算符重载,集合排序等基础语法及简单示例,方便以后查询。

1. 集合

1.1 使用集合
// 抽象动物基类
public abstract class Animal
{
	protected string name;
	public string Name
	{
		get { return name; }
		set { name = value; }
	}

	public Animal() => name = "The animal with no name";

	public Animal(string newName) => name = newName;
	public void Feed() => WriteLine($"{name} has been fed.");
}
// 奶牛子类
public class Cow:Animal
{
	public Cow(string newName) : base(newName) { }
	public void Milk() => WriteLine($"{name} has been milked.");
	
}
// 母鸡子类
public class Chicken : Animal
{
	public Chicken(string newName) : base(newName) { }
	public void LayEgg() => WriteLine($"{name} has laid an egg.");
}


static void Main(string[] args)
{
	WriteLine("Create an Array type collection of Animal objects and use it:");
	Animal[] animalArray = new Animal[2];
	animalArray[0] = new Cow("cow1");
	animalArray[1] = new Chicken("chicken1");
	animalArray[0].Feed();
	((Chicken)animalArray[1]).LayEgg();

	WriteLine("Create an ArrayList type collection of Animal objects and use it:");
	ArrayList animalArrayList = new ArrayList();
	Cow mycow = new Cow("cow2");
	animalArrayList.Add(mycow);
	animalArrayList.Add(new Chicken("chicken2"));

	WriteLine();
	foreach(Animal myAnimal in animalArrayList)
	{
		WriteLine($"New {myAnimal} object added to ArrayList collecion, Name={myAnimal.Name}");
	}
	WriteLine($"ArrayList collection contains {animalArrayList.Count} objects.");
	((Animal)animalArrayList[0]).Feed();
	((Chicken)animalArrayList[1]).LayEgg();

	animalArrayList.RemoveAt(1);
	((Animal)animalArrayList[0]).Feed();
	animalArrayList.AddRange(animalArray);
	mycow.Name = "cow22";
	foreach (Animal myAnimal in animalArrayList)
	{
		WriteLine($"New {myAnimal} object added to ArrayList collecion, Name={myAnimal.Name}");
	}
	ReadKey();
}

1.2 自定义集合

采用继承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();
}
1.3 键值对集合
// 自定义键值对集合
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; }
	}
	
	// 迭代器
	public new IEnumerator GetEnumerator()
	{
		foreach(object animal in Dictionary.Values)
		{
			yield return (Animal)animal;
		}
	}
}

static void Main(string[] args)
{
	Animals animalsDictionary = new Animals();
	animalsDictionary.Add("cow", new Cow("cow1"));
	animalsDictionary.Add("chicken", new Chicken("chicken1"));
	foreach(DictionaryEntry myEntry in animalsDictionary)
	{
		WriteLine($"New {myEntry.Value.ToString()} object added to custom collection," +
			$"Name={((Animal)myEntry.Value).Name}");
	}
	// 使用迭代器
	foreach(Animal animal in animalsDictionary)
	{
		Write($"{animal.Name}   ");
	}
	ReadKey();
}
1.4 迭代器

迭代器是一个代码块,按顺序提供了要在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();
}

1.5 深度复制

实现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();
}

2. 比较

2.1 装箱与拆箱

封箱是把值类型转换为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();
}
2.2 is运算符

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}");
}
2.3 运算符重载

通过运算符的重载,可以对我们设计的类使用标准的运算符。重载所有二元运算符都是一样的,一元运算符也是同样的,但只有一个参数。一些运算符如<和>必须成对重载。

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}");
2.4 对象集合排序

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}");
}

3. 转换运算符

如果要在不相关的类型之间转换,例如类型之间没有继承关系,也没有共享接口,就必须重载转换运算符。有几个注意事项:

  • 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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值