1、泛型
泛型是一种特殊的类型,它把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候进行。
例如,通过使用泛型类型参数 T,您可以编写其他客户端代码能够使用的单个类,而不致引入运行时强制转换或装箱操作的成本或风险,如下所示:
// 声明 generic 类 public class GenericList<T> { void Add(T input) { } } class TestGenericList { private class ExampleClass { } static void Main() { // 声明list为整数类型int GenericList<int> list1 = new GenericList<int>(); // 声明list为字符类型string GenericList<string> list2 = new GenericList<string>(); // 声明list为一个类 GenericList<ExampleClass> list3 = new GenericList<ExampleClass>(); } }
1、使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。
2、泛型最常见的用途是创建集合类。
3、.NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如 System.Collections 命名空间中的 ArrayList。
4、您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
5、可以对泛型类进行约束以访问特定数据类型的方法。
6、关于泛型数据类型中使用的类型的信息可在运行时通过反射获取
.NET Framework类库在Collections.Generic命名空间中包含几个泛型集合类。List<T>类是ArrayList类的泛型等效类。使用大小可按需动态增加的数组实现IList泛型接口。动态数组的好处是不必事先设置较大的数组,减少不必要的内存开销。
微软MSDN的C#编程指南建议使用泛型集合类,如List<T>类,而不要使用非泛型集合类,如ArrayList类,也不要自行创建集合类。原因是不必做.NET Framework已经完成的工作,公共语言运行库能够共享Microsoft中间语言代码和元素据,这是自己编写的强类型所无法做到的。
下面举例说明List<T>的用法。
public class Student //学生类作为数据源 { public string Name { get; set; } public List<int> Scores { get; set; } //成绩集合 } static void Main(string[] args) { //初始化泛型类List<Student>,集合中的元素包含一个成绩的内部序列List<int> List<Student> students = new List<Student>(); students.Add(new Student { Name = "张三", Scores = new List<int> { 93, 72, 88, 78 } }); students.Add(new Student { Name = "李四", Scores = new List<int> { 95, 71, 88, 68 } }); //使用Linq查询 var Query = from student in students where student.Scores.Average() >= 80 select new { 姓名 = student.Name, 成绩 = student.Scores.Average() }; foreach (var q in Query) Console.WriteLine("{0} {1}", q.姓名, q.成绩); } 输出:张三 82.75 / 李四 80.5
2、扩展方法
扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。
扩展方法让大家很容易的向现有类型中添加方法(不破坏源类的内容)
写法,看代码:
class Program { static void Main(string[] args) { Human human = new Human(); human.Name = "张三"; human.Age = 18; human.GetName(); human.GetAge("岁"); } } public class Human { public string Name { get; set; } public int Age { get; set; } } public static class HumanExtension { /// <summary> /// 扩展Human类的方法GetName /// </summary> /// <param name="human"></param> public static void GetName(this Human human) { Console.WriteLine("姓名:" + human.Name); } /// <summary> /// 扩展Human类的方法GetAge /// </summary> /// <param name="human"></param> /// <param name="unit">单位</param> public static void GetAge(this Human human, string unit) { Console.WriteLine("年龄:" + human.Age + unit); } }
很容易吧,需要注意的是,扩展方法必须是在非泛型静态类中定义,并且扩展方法必须是静态的,方法的第一个参数必须是this [类型]。使用扩展方法,可以很容易的为我们已有的类添加方法,如给String类添加个ToSource方法等。
原类调用这个扩展的方法时,要通过类的实例调用(尽管这个扩展方法是静态的)
Person.cs: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ccharp_001 { public class Person { public string name { get; set; } public string phone { get; set; } } } //扩展方法: PersonExtension.cs: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Ccharp_001 { public static class PersonExtension { public static string showInfo(this Person person) { return "姓名:"+person.name +" 电话:"+ person.phone; } } } //主函数的调用: class Program { static void Main(string[] args) { Person p = new Person { name="小王",phone="15800000000" }; Console.WriteLine(p.name ); Console.WriteLine("下面是扩展类的方法调用"); Console.WriteLine(p.showInfo ()); while (true) { } } }
注:如果扩展方法名与类中已有的方法名相同,则优先调用类中原来的方法。
3、委托
将方法作为方法的参数
先看一个例子:在屏幕上输出一句问候语:
public void GreetPeople( string name) { //.... EnglishGreeting(name); } public void EnglishGreeting( string name) { Console.WriteLine( "Morning," +name); } |
暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再次传递name参数,EnglishGreeting则用于向屏幕输出 “Morning, Jimmy”。
现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法:
public void ChineseGreeting(string name) { Console.WriteLine("早上好, " + name); }
这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据:
public enum Language { English, Chinese } public void GreetPeople(string name, Language lang) { //做某些额外的事情,比如初始化之类,此处略 switch(lang) { case Lang.English: EnglishGreeting(name); break; case Lang.Chinese: ChineseGreeting(name); break; } }
OK,尽管这样解决了问题,但我不说大家也很容易想到,这个解决方案的可扩展性很差,如果日后我们需要再添加韩文版、日文版,就不得不反复修改枚举和GreetPeople()方法,以适应新的需求。
在考虑新的解决方案之前,我们先看看 GreetPeople的方法签名:
public void GreetPeople(string name, Language lang)
我们仅看 string name,在这里,string 是参数类型,name 是参数变量,当我们赋给name字符串“jimmy”时,它就代表“jimmy”这个值;当我们赋给它“张子阳”时,它又代表着“张子阳”这个值。然后,我们可以在方法体内对这个name进行其他操作。哎,这简直是废话么,刚学程序就知道了。
如果你再仔细想想,假如GreetPeople()方法可以接受一个参数变量,这个变量可以代表另一个方法,当我们给这个变量赋值 EnglishGreeting的时候,它代表着 EnglishGreeting() 这个方法;当我们给它赋值ChineseGreeting 的时候,它又代表着ChineseGreeting()方法。我们将这个参数变量命名为 MakeGreeting,那么不是可以如同给name赋值时一样,在调用 GreetPeople()方法的时候,给这个MakeGreeting 参数也赋上值么(ChineseGreeting或者EnglishGreeting等)?然后,我们在方法体内,也可以像使用别的参数一样使用MakeGreeting。但是,由于MakeGreeting代表着一个方法,它的使用方式应该和它被赋的方法(比如ChineseGreeting)是一样的,比如:
MakeGreeting(name);
好了,有了思路了,我们现在就来改改GreetPeople()方法,那么它应该是这个样子了:
public void GreetPeople(string name, *** MakeGreeting) { MakeGreeting(name); }
public void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); }
using System; using System.Collections.Generic; using System.Text; namespace Delegate { //定义委托,它定义了可以代表的方法的类型 public delegate void GreetingDelegate(string name); class Program { private static void EnglishGreeting(string name) { Console.WriteLine("Morning, " + name); } private static void ChineseGreeting(string name) { Console.WriteLine("早上好, " + name); } //注意此方法,它接受一个GreetingDelegate类型的方法作为参数 private static void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); } static void Main(string[] args) { GreetPeople("Jimmy Zhang", EnglishGreeting); GreetPeople("张子阳", ChineseGreeting); Console.ReadKey(); } } } 输出如下: Morning, Jimmy Zhang 早上好, 张子阳
我们现在对委托做一个总结: