众所周知,数值型变量之间可以直接比较大小
如:
Int i=100;
Int j=100;
Console.Writeline(i>j); //输出false
之所以可以比较两个整型变量的大小,是因为在数学上两个整数之间谁大谁小有明确定义,比较两个整形变量时,实际上比较的是这两个整形变量所保存数值的大小。
然而同将同样的方法应用于引用类型的变量 ,问题来了,代码无法通过编译
Object o1=new object();
Object o2=new object();
Console.WriteLine(o1 > o2);
上述代码之所以不能通过编译,是因为两个变量引用了两个object类形的对像,而默认情况下两个object类型对像谁大谁小是未定义的。
但在某此具体的场景下,确定两个对像的大小是有意义的
比如判断两个圆的大小,半径相等的圆就可以认为是相等的圆,半径越大的圆也就越大,
如果用Circle类来表示一个圆,上述问题就转换为Circle对像的大小比较问题。
默认情况下,.Net Formework显示什么
Circle obj1 = new Circle() { Radio = 100 };
Circle obj2 = new Circle() { Radio = 100 };
Console.WriteLine(obj1 == obj2); //false
Console.WriteLine(obj1.Equals(obj2)); //false
虽然两个圆的半径是相等的,希望两个对像比较的结果是true,但由于对像变量obj1与obj2引用的是两个不同的对像,所以==返回false,equals也是比较两个对像是否引用同一对像,所以也返回false。
修改代码,实现IComparable接口,自定义一个CompareTo方法,用于比较大小
class Circle:IComparable
{
public double Radio = 0; //半径
public CircleCenter center; //圆心
public int CompareTo(object obj)
{
double ret = Math.Abs((obj as Circle).Radio - this.Radio);
if (ret==0)
return 0;//相等
if ((obj as Circle).Radio < this.Radio)
{
return 1;//大
}
return -1; //小
}
}
有许多情况,可能只需要知道两个对像是否相等,而不需要知道这两个对像谁大谁小,这时可以重写object类的equals方法
public override bool Equals(object obj)
{
if (this.CompareTo(obj) == 0)
{
return true;
}
return false;
}
重写或重载object.equals需满足三点
1.自反性:即自身与自身相等。a.Equals(a) 返回true
2.对称性:若a与b相等,则b与a也相等,这就是说a.Equals(b)与b.Equals(a)的值一次是相等的
3.传递性:若a与b相等,b与c相等,则a与c相等。这就是说如果a.Equals(b)和b.Equals(c)都为true,则a.Equals(c)的值也为true
重写了equals方法,马上就可以重载==运算符,若一个对像重载了==运算符,也必须重载!=运算符,这是一个编程的基本原则,
完整代码见下方
class Program
{
static void Main(string[] args)
{
Circle obj1 = new Circle() { Radio = 100 };
Circle obj2 = new Circle() { Radio = 100 };
Console.WriteLine(obj1.Equals(obj2)); //true
Console.WriteLine(obj1.CompareTo(obj2));//0
Console.WriteLine(obj1 != obj2); //false
Circle[] circles = new Circle[4]{new Circle{Radio=10},
new Circle{Radio=20},
new Circle{Radio=15},
new Circle{Radio=18}};
Array.Sort(circles);
foreach (Circle c in circles)
{
Console.WriteLine(c.Radio);
}
Console.ReadKey();
}
}
class Circle:IComparable
{
public double Radio = 0; //半径
// public CircleCenter center; //圆心
public int CompareTo(object obj)
{
double ret = Math.Abs((obj as Circle).Radio - this.Radio);
if (ret==0)
return 0;//相等
if ((obj as Circle).Radio < this.Radio)
{
return 1;//大
}
return -1; //小
}
//重写Equals方法
public override bool Equals(object obj)
{
if (this.CompareTo(obj) == 0)
{
return true;
}
return false;
}
//覆盖Object类的GetHashCode方法
public override int GetHashCode()
{
//整数部分与小数点后3位相同的对象生成相同的哈希值
return (int)(Radio * 1000);
}
//重载相关运算符
public static bool operator ==(Circle obj1, Circle obj2)
{
return obj1.Equals(obj2);
}
public static bool operator !=(Circle obj1, Circle obj2)
{
return !obj1.Equals(obj2);
}
public static bool operator >(Circle obj1, Circle obj2)
{
return obj1.CompareTo(obj2)==1;
}
public static bool operator <(Circle obj1, Circle obj2)
{
return obj1.CompareTo(obj2) == -1;
}
}
在某些情况下可能会需要将数组中的元素转换为另一个类型,这个工作可以通过调用数组基类Array的ConvertAll方法实现:
class Program
{
static void Main(string[] args)
{
int[] intArr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
double[] doublearr = Array.ConvertAll<int, double>(intArr, IntToDouble);
foreach (double d in doublearr)
{
Console.WriteLine(d);
}
Console.ReadKey();
}
static Converter<int, double> IntToDouble = delegate(int value)
{
return (double)value;
};
}
当需要循环迭代数组中的所有元素,并且对这些元素都进行同样的处理工作时,可以调用数组基类的Array的Foreach<T>方法达到目的
class Program
{
static void Main(string[] args)
{
//源数组
int[] SourceArr = new int[] { 0, 1, 2, 3, 4, 5, 6 };
//目标数组
int[] TargetArr = new int[SourceArr.Length];
int index=0;
Action<int> DoubleArr = delegate(int element)
{
TargetArr[index++] = element * 2;
};
//循环源数组,把数据存入目标数组
Array.ForEach<int>(SourceArr, DoubleArr);
Array.ForEach<int>(TargetArr,element => { Console.WriteLine(element); });
Console.ReadKey();
}
}
实际开发中数据排序很常见,数组基类Array直接就内置了相应的排序功能,比如选择择排序,冒泡排序
Sort方法是最简单的:例
class Program
{
static void Main(string[] args)
{
int[] IntArr = new int[10];
Random ran = new Random();
for (int i = 0; i < IntArr.Length; i++)
{
IntArr[i] = ran.Next(1, 100);
}
Array.Sort(IntArr);
Console.ReadKey();
}
}
当调用此方法时,要求数组中的元素要实现System.IComparable接口,否则此方法会抛出InvalidOperationException异常,如果数组中元素没有实现Icomparable接口 ,可以提供一个对像比较器完成排序,或者更简单些,不需要定义一个对像比较器,直接给Sort提供一个可以比较两对像大小的方法
class Program
{
static void Main(string[] args)
{
Random ran = new Random();
MyClass[] obj = new MyClass[10];
for (int i = 0; i < obj.Length; i++)
{
obj[i] = new MyClass { value = ran.Next(1, 100) };
}
//定义一个委托,用于比较两个对像大小,当x>y时,返回一个正数,当x=y时,返回0,当x<y时返回负数
Comparison<MyClass> WhoIsGreater = delegate(MyClass x, MyClass y)
{
if (x.value > y.value)
return 1;
else if (x.value == y.value)
return 0;
else
return -1;
};
//未排序前输出
Array.ForEach(obj, element => { Console.Write(element.value + ","); });
//排序
Array.Sort(obj,WhoIsGreater);
Console.WriteLine("");
//排序后输出
Array.ForEach(obj, element => { Console.Write(element.value + ","); });
Console.ReadKey();
}
}
Sort方法提供了多个形式的重载,有一个重载可以实现两个数组的联动,假设有两个一样长度的数组(分别叫keys和Itmes),这两个数组的元素是一一对应的,这就是说,keys数组的第一个元素对应Items数据中的第一个元素,对Keys数组中的元素进行排序同时也会更改Items中的元素位置,以维护这两个数据元素间的连动关系。示例中字符串数组存放代表一周的七个英文单词,而int数组则保存1-7个整数,并且与字符串数组的星期单词是连动的
class Program
{
static void Main(string[] args)
{
int[] Keys = new int[] { 7, 2, 3, 4, 1, 5, 6 };
string[] Items = new string[] { "Sunday", "Wednesssday", "Thursday", "Tuesday", "Monday", "Friday", "Saturday" };
//对keys排序,对应的items也会跟着变
Array.Sort<int,string>(Keys,Items);
Array.ForEach(Keys, element => { Console.Write(element + ","); });
Console.WriteLine("");
Array.ForEach(Items, element => { Console.Write(element + ","); });
Console.ReadKey();
}
}
有时可能希望保存在数组中的元素都满足特定的要求,数组基类Array提供了相应的静态方法完成这一工作
TrueForAll<T>方法所有的条件是否都满足,只要有一个不满足就返回false,代码示例如下:
class Program
{
static void Main(string[] args)
{
Predicate<int> match = delegate(int value)
{
return value % 2 == 0;
};
int[] IntArr=new int[]{0,1,2,3,4,5,6,7,8,9};
//判断数组元素是否都为偶数,true全部是偶数,如果有一个不是偶数即返回false
bool IsAllEven = Array.TrueForAll(IntArr, match);
}
}
判断数组中是否存在满足某个特定元素可以用Exists<T>,代码示例如下:
class Program
{
static void Main(string[] args)
{
int[] IntArr=new int[]{0,1,2,3,4,5,6,7,8,9};
Predicate<int> ExistEventNumber = delegate(int value)
{
if (value > 8)
return true;
return false;
};
bool IsExists = Array.Exists(IntArr, ExistEventNumber);
}
}
TrueForAll<T>跟Exists<T>区别在于:前者全部满足指定条件才返回true,后者只要有一个满足即返回true
数组基类Array还提供了一堆的方法用于查找元素
Find<T>:从数组开头查找匹配元素,FindLast<T>从数组结尾开始向前查找匹配元素
FindAll<T>:查找数据中满足特定条件的所有元素
FindIndex<T>:查找数组中满足特定条件的元素所在的位置(索引)
.Net基类库中大多数集合都实现了IEnumerable<T>接口,从.Net3.5开始,.Net基类库中提供了一组针对IEnumerable<T>接口的扩展方法,为各种对像集合提供各种标准查询服务,这些扩展方法返回一个对像集合,可以使用foreach语句逐个访问这些对像,或者将其用作数据绑定控件的数据源,这组扩展方法功能强大,使用灵活,实际构成了LINQ技术大厦的地基
筛选:是指从对像集合中选出那些满足特定条件的对像,可通过where扩展方法实现,下图程序是从指定路径中获取包含指定字符的文件名
完整代码如下:
class FileBroseHelper
{
/// <summary>
/// 取指定文件夹中的文件
/// </summary>
/// <param name="foldername"></param>
/// <returns></returns>
public static List<FileInfo> GetAllFileInFolder(string foldername)
{
try
{
List<FileInfo> files = null;
if (!Directory.Exists(foldername))
{
return null;
}
DirectoryInfo d = new DirectoryInfo(foldername);
files = d.GetFiles().ToList();
return files;
}
catch (Exception ex)
{
throw (ex);
}
}
/// <summary>
/// 将文件显示到listbox中
/// </summary>
/// <param name="file"></param>
/// <param name="list"></param>
public static void ShowFileListInListBox(IEnumerable<FileInfo> file, System.Windows.Forms.ListBox list)
{
list.Items.Clear();
foreach (FileInfo f in file)
{
list.Items.Add(f);
}
}
}
按钮事件如下:
private void btnClick_Click(object sender, EventArgs e)
{
if (txtFolderNam.Text.Trim().Length == 0)
{
return;
}
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
//获取选择的路径
string foldername = folderBrowserDialog1.SelectedPath;
labTitle.Text = foldername;
//取该路径下所有文件
List<FileInfo> files = FileBroseHelper.GetAllFileInFolder(foldername);
IEnumerable<FileInfo> ret = files.Where<FileInfo>(file =>file.Name.IndexOf(txtFolderNam.Text)!=-1);
//把筛选出来的文件名显示在listbox中
FileBroseHelper.ShowFileListInListBox(ret, listBox1);
}
}
上述代码中Lambda表达式给Where扩展方法赋值,用到是的FilInfo是System.IO命名空间中的类,封装了文件的基本信息和常用操作。
投影:指把某对像集合中对像的部分属性抽取出来
数据转换:指将某对像集合中感兴趣的对像(或抽取它的部分字段/属性)转换为另一种类型的对像
代码见下方:
class Program
{
static void Main(string[] args)
{
//转换为不同类型的集合
IEnumerable<string> filelist = Directory.GetFiles("c:\\", "*.*");
IEnumerable<FileInfo> files = filelist.Select(file => new FileInfo(file));
foreach (FileInfo f in files)
{
Console.WriteLine(f);
}
//返回一个匿名对像的集合
var items =filelist.Select(file=>{
FileInfo f=new FileInfo(file);
return new { FileName = f.Name, Size = f.Length };
});
foreach (var i in items)
{
Console.WriteLine(i.FileName + "," + i.Size);
}
Console.ReadKey();
}
}
排序:使用扩展方法OrderBy(升序),OrderByDescending(降序)进行排序
代码见下方:
class Program
{
static void Main(string[] args)
{
Pet[] pets = new Pet[]{new Pet{Name="Tiger",Age=22},
new Pet{Name="Lion",Age=17},
new Pet{Name="rabbit",Age=18}};
IEnumerable<Pet> query = pets.OrderBy(pet => pet.Age);
foreach (var q in query)
{
Console.WriteLine(q.Age);
}
Console.ReadKey();
}
}
class Pet
{
public string Name { get; set; }
public int Age { get; set; }
}
数据连接:指根据某个对像的属性将两个数据集合的对像连接起来,得到一个新的集合
class Program
{
static void Main(string[] args)
{
A[] a = new A[10];
B[] b = new B[10];
for (int i = 0; i <10; i++)
{
a[i] = new A { AID = i, AName = "AName" + i.ToString() };
b[i] = new B { BID = i, BName = "BName" + i.ToString() };
}
var result = a.Join(b, aobj => aobj.AID, bobj => bobj.BID, (aobj, bobj) => new { aobj.AID, aobj.AName, bobj.BName });
foreach (var elem in result)
{
Console.WriteLine("{0},{1},{2}", elem.AID, elem.AName, elem.BName);
}
Console.ReadKey();
}
}
class A
{
public int AID;
public string AName;
}
class B
{
public int BID;
public string BName;
}
集合运算:常用的集合运算包括求交集,并集,差集等,.Net基类库提供了一组扩展方法来完成标准集合运算,例如union,contact,sum,intersect等
示例代码如下:
class Program
{
static void Main(string[] args)
{
//创建一个多态对象集合
IEnumerable<object> stuff = new object[] { new object(), 1, 3, 5, 7, 9, "\"thing\"", Guid.NewGuid() };
//输出数组的内容
Print("Stuff集合的内容: {0}", stuff);
//偶数集合
IEnumerable<int> even = new int[] { 0, 2, 4, 6, 8 };
Print("偶数集合的内容: {0}", even);
//从多态集合stuff中筛选中整数元素(全部为奇数)组成一个新集合
IEnumerable<int> odd = stuff.OfType<int>();
Print("奇数集合的内容: {0}", odd);
//求两个集合的并集
IEnumerable<int> numbers = even.Union(odd);
Print("奇数与偶数集合的并集,成为一个整数集合: {0}", numbers);
Print("整数集合与偶数集合的并集: {0}", numbers.Union(even));
Print("整数集合与奇数集合相连接: {0}", numbers.Concat(odd));
Print("整数集合与偶数集合的交集: {0}", numbers.Intersect(even));
Print("整数集合与奇数集合连接,再删除重复值: {0}", numbers.Concat(odd).Distinct());
if (!numbers.SequenceEqual(numbers.Concat(odd).Distinct()))
{
throw new Exception("Unexpectedly unequal");
}
else
{
Print("反转整数集合: {0}", numbers.Reverse());
Print("求整数集合的平均值: {0}", numbers.Average());
Print("求整数集合的总和: {0}", numbers.Sum());
Print("求整数集合的最大值: {0}", numbers.Max());
Print("求整数集合的最小值: {0}", numbers.Min());
}
Console.ReadKey();
}
private static void Print<T>(string format, IEnumerable<T> items)
{
StringBuilder text = new StringBuilder();
foreach (T item in items.Take(items.Count() - 1))
{
text.Append(item + ", ");
}
text.Append(items.Last());
Console.WriteLine(format, text);
}
private static void Print<T>(string format, T item)
{
Console.WriteLine(format, item);
}
}