关于对象集合很常用的方法
本文涉及:
1.对列表中的元素进行“批量类型转换”
2.对列表中每个元素的“加工”
3.对列表元素的排序
4.检测列表中的元素是否满足某个条件
5.在列表中查找元素
注:上述的操作并不是针对数组,或者List..而是有通用性,一般实现了IEnumerable<T>接口的列表都可具有下面的方法。
一、对列表中的元素进行“批量类型转换”
在某些情况下可能会需要将列表中的所有元素转换为另一个类型,这个工作可以通过调用 ConvertAll 方法实现。
public List<TOutput> ConvertAll<TOutput>(
Converter<T, TOutput> converter
)
上述方法的第2个参数是一个委托,其定义如下:
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter);
通过设计一个满足此委托要求的数组元素类型转换方法,ConvertAll 方法可以将一个集合中的所有元素转换类型后再复制到一个新的集合中。
示例代码:
using System;
using System.Collections.Generic;
public class Example
{
public class PointF
{
public PointF(float x1, float y1)
{
this.X1 = x1;
this.Y1 = y1;
}
public float X1;
public float Y1;
}
public class Point
{
public Point(int x2, int y2)
{
this.X2 = x2;
this.Y2 = y2;
}
public int X2;
public int Y2;
}
public static void Main()
{
List<PointF> lpf = new List<PointF>();
lpf.Add(new PointF(27.8F, 32.62F));
lpf.Add(new PointF(99.3F, 147.273F));
lpf.Add(new PointF(7.5F, 1412.2F));
Console.WriteLine();
foreach (PointF p in lpf)
{
Console.WriteLine(p.X1.ToString()+ "" + p.Y1.ToString());
}
//调用方式一:标准写法
List<Point> lp = lpf.ConvertAll(
new Converter<PointF, Point>(PointFToPoint));
//调用方式二:使用匿名方法(可以省去单独定义的PointFToPoint方法)
//使用匿名方法需要注意,方法体以 delegate 关键字打头,
//其后是参数列表,紧接着在一对大括号内书写方法代码,最后以分号结尾
List<Point> lp2 = lpf.ConvertAll(
new Converter<PointF, Point>(
delegate(PointF pf)
{
return new Point((int)pf.X1, (int)pf.Y1);
}));
//调用方式三:使用Lambda表达式再次精简代码
List<Point> lp3 = lpf.ConvertAll(
new Converter<PointF, Point>(
(pf) => { return new Point((int)pf.X1, (int)pf.Y1); }));
Console.WriteLine();
foreach (Point p in lp)
{
Console.WriteLine(p.X2.ToString() + "" + p.Y2.ToString());
}
Console.ReadKey();
}
//这里定义了一个转换方法,与Converter委托相对应。我们可以叫这个方法为“元素转换器”
public static Point PointFToPoint(PointF pf)
{
return new Point(((int)pf.X1), ((int)pf.Y1));
}
}
/* This code example produces the following output:
{X=27.8, Y=32.62}
{X=99.3, Y=147.273}
{X=7.5, Y=1412.2}
{X=27,Y=32}
{X=99,Y=147}
{X=7,Y=1412}
*/
Converter 委托是C# 里面位于System命名空间下的系统委托,主要用来转换元素类型。
二、对列表中每个元素的“加工”
一般我们要对集合中的元素逐个进行同样处理工作时,我们会写一个foreach循环来处理,但这样造成了代码可读性低下。其实.net中为我们提供了foreach<T>方法
方法原型如下:
public void ForEach(
Action<T> action
)
其参数是一个action委托,它引用“施加于”每个数组元素的“处理方法”。
示例代码:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<String> names = new List<String>();
names.Add("Bruce");
names.Add("Alfred");
names.Add("Tim");
names.Add("Richard");
//这里循环list每个元素,调用print方法
//调用方法一
names.ForEach(Print);
//调用方法二:匿名方法
names.ForEach(delegate(String name)
{
Console.WriteLine(name);
});
//调用方式三:使用 Lambda表达式精简代码,可以省略Print方法。
names.ForEach(r => Console.WriteLine(r));
Console.ReadKey();
}
private static void Print(string s)
{
Console.WriteLine(s);
}
}
/* This code will produce output similar to the following:
* Bruce
* Alfred
* Tim
* Richard
* Bruce
* Alfred
* Tim
* Richard
*/
三、对列表元素的排序
集合的排序前面的日记都有记载。详情见前面的日记。
这里做一下总结:
MSDN上List有如下的sort重载:
如果调用无参的sort()方法,那么要求集合中的元素要实现 System.IComparable 接口,否则此方法会抛出InvalidOperationException异常。
如果集合的元素没有实现IComparable接口,则可以调用Sort(IComparer<T>),这时我们要创建一个类实现IComparer接口作为比较器来完成排序。
或者更为简单些,不需要定义一个比较器,直接给sort方法提供一个用于"比较两对象”大小的方法即可---实现Comparison<T>委托。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SortObjectArray
{
class Program
{
static void Main(string[] args)
{
MyClass[] Objs = new MyClass[10];
Random ran=new Random();
for (int i = 0; i < 10; i++)
Objs[i] = new MyClass { Value = ran.Next(1, 100) };
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.Sort<MyClass>(Objs,WhoIsGreater);
Array.ForEach<MyClass>(Objs,(obj)=>{Console.WriteLine(obj.Value);});
Console.ReadKey();
}
}
class MyClass
{
public int Value;
}
}
四、关于Prddicate<T>委托
使用该委托可以实现很多实现,比如:
A.检测列表中的元素是否满足某个条件 TrueForAll<T>
B.在集合中查找元素 Exists<T>
C.查找满足条件的元素 Find<T> FindLast<T> FindAll<T>
D.查找满足条件的元素的索引 FindIndex
.......等等
如果 集合
中的每个元素都与指定的谓词所定义的条件相匹配,则为 true;否则为 false。 如果列表不包含任何元素,则返回值为 true。
Predicate<T>是一个委托,它引用一个返回bool值的方法,此方法代表集合元素必须满足的条件。
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
List<string> dinosaurs = new List<string>();
dinosaurs.Add("Compsognathus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("Oviraptor");
dinosaurs.Add("Velociraptor");
dinosaurs.Add("Deinonychus");
dinosaurs.Add("Dilophosaurus");
dinosaurs.Add("Gallimimus");
dinosaurs.Add("Triceratops");
Console.WriteLine();
foreach(string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}
Console.WriteLine("\nTrueForAll(EndsWithSaurus): {0}",
dinosaurs.TrueForAll(EndsWithSaurus));
Console.WriteLine("\nFind(EndsWithSaurus): {0}",
dinosaurs.Find(EndsWithSaurus));
Console.WriteLine("\nFindLast(EndsWithSaurus): {0}",
dinosaurs.FindLast(EndsWithSaurus));
Console.WriteLine("\nFindAll(EndsWithSaurus):");
List<string> sublist = dinosaurs.FindAll(EndsWithSaurus);
foreach(string dinosaur in sublist)
{
Console.WriteLine(dinosaur);
}
Console.WriteLine(
"\n{0} elements removed by RemoveAll(EndsWithSaurus).",
dinosaurs.RemoveAll(EndsWithSaurus));
Console.WriteLine("\nList now contains:");
foreach(string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}
Console.WriteLine("\nExists(EndsWithSaurus): {0}",
dinosaurs.Exists(EndsWithSaurus));
}
// Search predicate returns true if a string ends in "saurus".
private static bool EndsWithSaurus(String s)
{
if ((s.Length > 5) &&
(s.Substring(s.Length - 6).ToLower() == "saurus"))
{
return true;
}
else
{
return false;
}
}
}
/* This code example produces the following output:
Compsognathus
Amargasaurus
Oviraptor
Velociraptor
Deinonychus
Dilophosaurus
Gallimimus
Triceratops
TrueForAll(EndsWithSaurus): False
Find(EndsWithSaurus): Amargasaurus
FindLast(EndsWithSaurus): Dilophosaurus
FindAll(EndsWithSaurus):
Amargasaurus
Dilophosaurus
2 elements removed by RemoveAll(EndsWithSaurus).
List now contains:
Compsognathus
Oviraptor
Velociraptor
Deinonychus
Gallimimus
Triceratops
Exists(EndsWithSaurus): False
*/