编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]
前言
泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能。基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用。同时,它减少了泛型类及泛型方法中的转型,确保了类型安全。委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,它是对方法的引用。事件本身也是委托,它是委托组,C#中提供了关键字event来对事件进行特别区分。一旦我们开始编写稍微复杂的C#代码,就肯定离不开泛型、委托和事件。本章将针对这三个方面进行说明。
本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:
建议35、使用default为泛型类型指定初始值
建议36、使用FCL中的委托声明
建议37、使用lambda表达式代替方法和匿名方法
建议35、使用default为泛型类型指定初始值
有些算法,比如泛型集合List<T>的Find算法,所查找的对象有可能会是值类型,也有可能是引用类型。在这种算法内部,我们常常会为这些值类型变量或引用类型变量指定默认值。于是,问题来了:值类型变来那个的默认初始值是0值,而引用类型变量的默认初始值是null值,显然,这会导致下面编译出错:
C#编译器会阻止这样的代码通过编译。要让编译器接收为一个泛型类型参数指定一个初始值,最妥当的办法就是使用default关键字。所以,在上面的代码应该改为:
public T Func<T>() { T t = default(T); return t; }
这样如果它在运行时碰到的T是一个整型,那么运行时会为其赋值0;如果T在运行时是一个Person这样的引用类型,则会为其赋null值。
建议36、使用FCL中的委托声明
要注意FCL中存在三类这样的委托声明,他们分别是:Action、Func、Predicate。尤其是在他们的泛型版本出来以后,已经能够满足我们在实际编码过程中的大部分需要。
这里是之前一篇关于Action、Func、Predicate的有关介绍http://www.cnblogs.com/aehyok/p/3382291.html
除了Action、Func、Predicate外,FCL中还有用于标识特殊含义的委托声明。如用于表示注册事件方法的委托声明:
public delegate void EventHandler(object sender,EventArgs e); public delegate void EnentHandler<TEventArgs>(object sender,TEventArgs e);
表示线程的委托声明:
public delegate void ThreadStart(); public delegate void ParameterizedThreadStart(object obj);
表示异步回调的委托声明:
public delegate void AsyncCallback(IAsyncResult ar);
在FCL中没一类委托声明都代表一类特殊的用途,虽然可以使用自己的委托声明来代替,但是这样做不仅没有必要,而且会让代码失去简洁性和标准型。在我们实现自己的委托声明前,应该首先查看MSDN,确信有必要之后才这样做。
建议37、使用lambda表达式代替方法和匿名方法
首先我们使用Action和Func来做一个简单的小例子,控制台应用程序代码如下所示:
第一个版本
class Program { static int Add(int i, int j) { return i + j; } static void Print(string message) { Console.WriteLine(message); } static void Main(string[] args) { Func<int, int, int> add = Add; Action<string> print = Print; print(add(4, 5).ToString()); Console.ReadLine(); } }
实际上要完成上面的功能,还有多种编码方式,先来看一种最中规中矩的方式,同时也是最繁琐的写法:
第二个版本
class Program { static int Add(int i, int j) { return i + j; } static void Print(string message) { Console.WriteLine(message); } static void Main(string[] args) { Func<int, int, int> add = new Func<int,int,int>(Add); Action<string> print =new Action<string>(Print); print(add(4, 5).ToString()); Console.ReadLine(); } }
其实也就是将第一个版本稍作调整。从上面的写法中也可以看出:Add方法和Print方法实际上都只有一条语句,因此,使用匿名方法也许是一种更好的选择:
第三个版本
static void Main(string[] args) { Func<int, int, int> add = new Func<int,int,int>(delegate(int i,int j) { return i+j; }); Action<string> print =new Action<string>(delegate(string message) { Console.WriteLine(message); }); print(add(4, 5).ToString()); Console.ReadLine(); }
使用匿名方法以后,我们不需要在Main方法外部声明两个方法了,可以直接在Main这个工作方法中完成所有的代码编写,而且不会影响代码清晰性。实际上,所有代码行数不超过3行的方法(条件是它不倍重用),我们都建议采用这种方式来编写。上面的版本继续改进:
第四个版本
static void Main(string[] args) { Func<int, int, int> add = delegate(int i,int j) { return i+j; }; Action<string> print =delegate(string message) { Console.WriteLine(message); }; print(add(4, 5).ToString()); Console.ReadLine(); }
以上代码看上去更简化了,不过,最终极的改进是使用lambda表达式:
第五个版本
static void Main(string[] args) { Func<int, int, int> add = (x, y) => x + y; Action<string> print = (message) => Console.WriteLine(message); print(add(4, 5).ToString()); Console.ReadLine(); }
Lambda表达式操作符”=>“的左侧是方法的参数,右侧是方法体,其本质是匿名方法。实际上,经过编译后Lambda表达式就是一个匿名方法。我们应该在实际的编码工作中熟练运用它,避免写出繁琐且不美观的代码。