【整理】.Net Framework3.5之扩展方法、分部方法、Lambda表达式和Lambda语句
Func<T,TResult>泛型委托
在下面的扩展方法当中,要用到这方面的东西,所以在这里我简单说一下。Func<T,TResult>是泛型委托的一种演化版本,这种结构通常的说法是:封装一个具有类型T的参数,返回值为TResult类型的委托。下来我们一步一步看,它和传统委托之间的关系。
通常,我们传统的委托,使用方式如下,下面实现将一个字符串转换成大写字符串输出:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
delegate string ConvertMethod(string inString);
private static string UppercaseString(string inputString)
{
return inputString.ToUpper();
}
static void Main(string[] args)
{
//下面的也可以
//ConvertMethod convertMethod = UppercaseString;
ConvertMethod convertMethod = new ConvertMethod(UppercaseString);
string inputStr = "HeLlo wOrLD";
Console.WriteLine(convertMethod(inputStr));
Console.ReadLine();
}
}
}
将上面的ConvertMethod convertMethod=new ConvertMethod(UppercaseString)改为Func<T,TResult>结构
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
delegate string ConvertMethod(string inString);
private static string UppercaseString(string inputString)
{
return inputString.ToUpper();
}
static void Main(string[] args)
{
//下面的也可以
//ConvertMethod convertMethod = UppercaseString;
//ConvertMethod convertMethod = new ConvertMethod(UppercaseString);
Func<string, string> convertMethod = UppercaseString;
string inputStr = "HeLlo wOrLD";
Console.WriteLine(convertMethod(inputStr));
Console.ReadLine();
}
}
}
使用匿名委托修改上述程序如下,是不是很简洁呢
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
static void Main(string[] args)
{
Func<string, string> convertMethod = delegate(string inString) {
return inString.ToUpper();
};
string inputStr = "HeLlo wOrLD";
Console.WriteLine(convertMethod(inputStr));
Console.ReadLine();
}
}
}
最后,我们用最简洁的Lambda表达式修改上述代码如下
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
static void Main(string[] args)
{
Func<string, string> convertMethod = instring => instring.ToUpper();
string inputStr = "HeLlo wOrLD";
Console.WriteLine(convertMethod(inputStr));
Console.ReadLine();
}
}
}
扩展方法
-扩展方法使您能够向现有类型"添加"方法,而无须创建新的派生类、重新编译或以其他的方式修改原始类型
-扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样被调用,如下ints.OrderBy(g=>g):
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
static void Main(string[] args)
{
int[] ints = { 10, 15, 17, 16, 11, 23 };
var result = ints.OrderBy(g => g);
foreach (var i in result)
{
System.Console.WriteLine("" + i);
}
Console.ReadLine();
}
}
}
等价于
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
static void Main(string[] args)
{
int[] ints = { 10, 15, 17, 16, 11, 23 };
var result = ints.OrderBy(
delegate(int g)
{
return g;
});
foreach (var i in result)
{
System.Console.WriteLine("" + i);
}
Console.ReadLine();
}
}
}
-对于C#和VB编写的客户端代码,调用扩展方法和调用在类中实际定义的方法之间没有任何明显的区别
-编译器生成的中间语言会将代码转换为对静态方法的调用。因此,并未违反真正的封装原则
-扩展方法无法访问它们扩展的类型中的私有变量
-可以使用扩展方法来扩展类或接口,但不能重写扩展方法
-与类或接口方法具有相同签名和名称的扩展方法永远无法被执行
-在编译时,扩展方法的优先级总是比类中的方法的优先级低
-当编译时遇到方法调用时,编译器首先在类本身的方法中寻找调用的方法,如果找不到,才在扩展方法中寻找
最常用的扩展方法是LINQ标准查询运算符,它们向现有的System.Collections.IEnumerable和System.Collections.IEnumerable<T>类型添加了查询功能,如下:
注意:凡是扩展方法都用图标为前缀
自定义扩展方法
-扩展方法被定义为静态方法,但他们是通过实例方法语法进行调用的
-第一个参数指定该方法用于那种类型,并且该参数以this修饰符为前缀
-仅当您使用using指令将命名空间显式导入到源代码之后,扩展方法才位于范围中
-它是在非嵌套(即必须为顶级类)、非泛型静态类内部定义的
自定义扩展方法演示代码
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
static void Main(string[] args)
{
string str = "Hello Extensions Methods";
int i = str.WordCount();
Console.WriteLine(i);
Console.ReadLine();
}
}
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
定义
-分部方法使类的某个部分的实施者能够定义方法(类似于事件)。类的另一部分实施者可以决定是否实现该方法。
-如果调用了未实现的方法,将不会导致编译时错误或运行时错误。
-分部方法声明由两个部分组成:定义和实现。它们可以位于分部类的不同部分中,也可以位于同一部分中。如果不存在实现声明,则编译器将优化定义声明和对方的所有调用。如下:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
public partial class Employee
{
//Method方法定义
partial void Method();
public void test()
{
Method();
}
}
public partial class Employee
{
//Method方法实现
partial void Method()
{
Console.WriteLine("Hello World");
Console.ReadLine();
}
}
static void Main(string[] args)
{
Employee e = new Employee();
e.test();
}
}
}
Lambda表达式
-Lambda表达式是一个匿名方法,它可以包含表达式和语句,并且也可以用于创建委托或表达式目录树类型
-所有的Lambda表达式都使用Lambda运算符"=>",该运算符读作"goes to",该Lambda表达式的左边是输入参数(如果有),右边包含表达式或语句块。"x=>x*x"读作"x goes to x times x"。
-在is或as运算符的左侧不允许使用Lambda
-适用于匿名方法的所有限制也适用于Lambda表达式
-Lambda表达式只有一个输入参数的时候,输入参数的括弧才是可选的;如果两个或两个以上则必须使用括弧将输入参数括起,例如:(x,y)=>x*y
-编译器无法或难于判断输入参数类型,则使用显式指定类型,例如:(int x,string s)=>s.Length>x
-使用空括弧包含零输入参数,Lambda表达式的主题可以包含方法调用,例如:()=>SomeMethod()
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DotNetFrameworkDemo
{
class Program
{
delegate int Muti(int x);
static void Main(string[] args)
{
Muti muti1 = delegate(int x) {
return x * x;
};
Muti muti2 = x => x * x;
//上述muti1与muti2等价
Console.WriteLine(muti1(5));
Console.WriteLine(muti2(5));
Console.ReadLine();
}
}
}
Lambda语句
-Lambda语句与Lambda表达式很类似,只是主体语句包含在"{}"中,如:(input parameters)=>{statement;}
-理论上Lambda语句的主体语句可以包含任意数量的语句;但是实际上不会多于两到3条。
-像匿名方法一样,Lambda表达式无法用于创建表达式目录树
Lambda中的类型推理
在编写Lambda表达式时,通常不必为输入参数指定类型,因为编译器可以基于Lambda主体、基础委托类型以及C#3.0语言规范描述的其他因素推断类型
对于大多数标准查询运算符,第一个输入的是源序列中的元素的类型。因此,如果要查询IEnumerable<Person>的时候,第一个输入的元素将被视为Person对象。var results=people.where(p=>p.LastName=="White")
Lambda的一般规则:
1.Lambda包含的参数数量必须同委托类型包含的参数数量相同
2.Lambda中的每个输入参数都必须能够隐式的转换为委托类型的输入参数
3.如果Lambda中有返回值,则必须同委托类型的返回值相同