c#学习<四>:C#2.0、C#3.0
委托的演变
委托(C#1.0)
委托可看作是只定义了一个方法的接口,将委托的实例看作实现了这个接口的一个对象。
委托的执行要满足4个条件:
1. 声明委托类型 : delegate void StringProcessor(string input);
2. 必须有一个方法包含了要执行的代码 : void PrintString(string x){ ……}
3. 创建一个委托实例 : StringProcessor pro1 = new StringProcessor(X.PrintString);
4. 调用委托实例。 : pro1.Invoke("Hello"); 可简写为 pro1("Hello");
委托的删除、合并
委托实例实际上有一个操作列表与之关联。这称为委托实例的调用列表(invocation list)。
System.Delegate 类型中有提供静态方法Combine 和Remove分别负责“将两个委托实例(同一委托类型)调用列表连接到一起” 和“从一个委托实例中删除另一个实例的调用列表”。
类似于字符串,委托同样是不易变的,从而保障一致性。线程安全性。Delegate.Combine 从功能上讲和 String.Concat很像,都是合并现有实例来形成一个新的实例。
一般不会显式地调用Delegate.Combine,而是使用 + 或+= 操作符。Delegate.Remove则使用 - 或 -=操作符。
调用委托实例时,它的所有操作都顺序执行。如果委托的签名具有一个非void的返回类型。则invoke的返回值是最后一个操作的返回值。
事件
事件就是发生了某件事,然后采取针对性的措施。比如点击了刷新按钮,页面就执行一次刷新操作。
事件涉及两类角色: 事件发布者和事件订阅者。当某事件发生时,事件发布者会发布消息;事件订阅者会接收到事件已发生的通知,并做出相应处理。其中,触发事件的对象是事件发布者,捕获事件并对其做出处理的对象是事件订阅者。
使用发布/订阅模式(观察者模式)自定义一个事件
namespace EventTest.Test1 { public class Bridegroom { //自定义委托 public delegate void MarryHandler(string msg); //使用自定义委托类型定义事件 public event MarryHandler MarryEvent; //发出事件 public void OnMarriageComing(string msg) { //if (MarryEvent != null) //{ // MarryEvent(msg); //} //简写为 MarryEvent?.Invoke(msg); } } public class Friend { public string Name { get; private set; } public Friend(string name) { Name = name; } //事件处理函数,该函数需要符合MarryHandler委托的定义 public void SendMessage(string msg) { Console.WriteLine(msg); Console.WriteLine(this.Name + "收到了,到时候准时参加!"); } } }
调用
class Program { static void Main(string[] args) { Bridegroom bridegroom = new Bridegroom(); Friend friend1 = new Friend("张三"); Friend friend2 = new Friend("李四"); Friend friend3 = new Friend("王五"); bridegroom.MarryEvent += new Bridegroom.MarryHandler(friend1.SendMessage); bridegroom.MarryEvent += new Bridegroom.MarryHandler(friend2.SendMessage); bridegroom.OnMarriageComing("朋友们,我结婚了,红包拿来哦~~"); Console.WriteLine(System.Environment.NewLine); bridegroom.MarryEvent -= new Bridegroom.MarryHandler(friend2.SendMessage); bridegroom.MarryEvent += new Bridegroom.MarryHandler(friend3.SendMessage); bridegroom.OnMarriageComing("朋友们,我结婚了,红包拿来哦~~"); Console.ReadKey(); } }
.net类库中已经预定义了委托类型 EventHandler,在实际开发中一般使用后者来定义事件。
namespace EventTest.Test2 { public class Bridegroom { //使用自定义委托类型定义事件 public event EventHandler MarryEvent; //发出事件 public void OnMarriageComing(string msg) { Console.WriteLine(msg); MarryEvent?.Invoke(this,new EventArgs()); } } public class Friend { public string Name { get; private set; } public Friend(string name) { Name = name; } //事件处理函数,该函数需要符合MarryHandler委托的定义 public void SendMessage(object sender, EventArgs e) { Console.WriteLine(this.Name + "收到了,到时候准时参加!"); } } }
class Program { static void Main(string[] args) { Bridegroom bridegroom = new Bridegroom(); Friend friend1 = new Friend("张三"); Friend friend2 = new Friend("李四"); Friend friend3 = new Friend("王五"); bridegroom.MarryEvent += new EventHandler(friend1.SendMessage); bridegroom.MarryEvent += new EventHandler(friend2.SendMessage); bridegroom.OnMarriageComing("朋友们,我结婚了,红包拿来哦~~"); Console.WriteLine(System.Environment.NewLine); bridegroom.MarryEvent -= new EventHandler(friend2.SendMessage); bridegroom.MarryEvent += new EventHandler(friend3.SendMessage); bridegroom.OnMarriageComing("朋友们,我结婚了,红包拿来哦~~"); Console.ReadKey(); } }
但EventHander是用于处理不包含事件数据的事件。如果要在事件中包含数据,则要派生EventArgs类。
namespace EventTest.Test3 { public class MarryEventArgs : EventArgs { public string Msg { get; private set; } public MarryEventArgs(string msg) { Msg = msg; } } public class Bridegroom { //自定义委托类型 public delegate void MarryHandler(object sender, MarryEventArgs e); //使用自定义委托类型定义事件 public event MarryHandler MarryEvent; //发出事件 public void OnMarriageComing(string msg) { MarryEvent?.Invoke(this,new MarryEventArgs(msg)); } } public class Friend { public string Name { get; private set; } public Friend(string name) { Name = name; } //事件处理函数,该函数需要符合MarryHandler委托的定义 public void SendMessage(object sender, MarryEventArgs e) { Console.WriteLine(e.Msg); Console.WriteLine(this.Name + "收到了,到时候准时参加!"); } } }
namespace EventTest { class Program { static void Main(string[] args) { Bridegroom bridegroom = new Bridegroom(); Friend friend1 = new Friend("张三"); Friend friend2 = new Friend("李四"); Friend friend3 = new Friend("王五"); bridegroom.MarryEvent += new Bridegroom.MarryHandler(friend1.SendMessage); bridegroom.MarryEvent += new Bridegroom.MarryHandler(friend2.SendMessage); bridegroom.OnMarriageComing("朋友们,我结婚了,红包拿来哦~~"); Console.WriteLine(Environment.NewLine); bridegroom.MarryEvent -= new Bridegroom.MarryHandler(friend2.SendMessage); bridegroom.MarryEvent += new Bridegroom.MarryHandler(friend3.SendMessage); bridegroom.OnMarriageComing("朋友们,我结婚了,红包拿来哦~~"); Console.ReadKey(); } } }
事件与委托的关系
事件是基于委托实现的。从本质上讲,事件是一个特殊的委托。查看事件的IL代码,会发现它默认含有一个私有的委托类型变量,该变量用于保存对事件处理方法的引用。而访问这个变量只能通过定义该事件的类中的两个类似访问器一样的公共方法来进行间接访问(add_MarryEvent和remove_MarryEvent)。
可以把事件看作是类似于property的一种形式。事件存在的首要理由和属性一样,它们添加一个封装层,实现发布/订阅模式。
通常我们不希望外部的代码可以直接设置字段值,最起码也要先由所有者对新值进行验证。同样的我们也不希望外部代码可以随意地更改或调用一个事件的处理程序。只对外呈现事件本身,外部的代码就只能添加和移除事件的处理程序。
匿名方法(C#2.0)
C#2.0中对委托做了改进。
1. 为创建委托实例提供了一种简化形式。
handler = new EventHandler(HandlerDemoEvent);
简化为 handler = HandlerDemoEvent;
2. 增加Action,Predicate,Comparison三种泛型委托类型。增加匿名方法。
Predicate<T>用于FindAll,Comparison<T>用于Sort,Action<T>用于ForEach
通过匿名方法使代码更清晰,在操作的位置就可以知道这个行动具体是干什么的。
handler = delegate(object sender,EventArgs e){ Console.WriteLine("…………"); }; Predicate<int> predicate = delegate { return true; };
3. 支持参数逆变和返回值协变。
4. 可以使用泛型委托类型
Lambda表达式(C#3.0)
C#3.0中引入的Lambda表达式进一步简化了匿名方法的书写,使代码更简洁。
.Net3.5引入名为Func的泛型委托类型。
C#4.0中支持在委托中使用泛型的协变和逆变。
C#2.0
泛型
解决的问题
1. 提供了代码重用的另一种机制,使代码更富有表现力。
泛型所提供的代码重用是算法的重用。使类型可以参数化,从而不必为不同类型提供特殊的版本实现。
2. 提供了更好的性能。
引用类型和值类型之间存在着相互转换,这种装箱拆箱的操作会引起一定的性能损失。而泛型是避免性能损失的有效方法。
C#1.0 中ArrayList 里存放的是object, 将整数放入其中,会存在装箱操作。而采用List<int>的形式则不再需要类型转换。
3. 类型安全特性
C#1.0 中存在大量的强制类型转换。强制转换就是在说“程序员比编译器知道更多”,泛型的引入。将安全检查从执行时转移到了编译时。
泛型中的静态字段和静态函数
在泛型类型中,每个封闭的泛型类型中都有仅属于它自己的静态数据。
namespace Generic { public class TypeWithStaticField<T> { public static string Field { get; set; } public static void OutField() { Console.WriteLine(Field+":"+typeof(T).Name); } } class Program { static void Main(string[] args) { TypeWithStaticField<int>.Field = "一"; TypeWithStaticField<string>.Field = "二"; TypeWithStaticField<Guid>.Field = "三"; TypeWithStaticField<int>.OutField(); TypeWithStaticField<string>.OutField(); TypeWithStaticField<Guid>.OutField(); Console.ReadKey(); } } }
在使用实际类型参数代替泛型参数时,编译器会根据不同的类型实参,重新生成类型。当将int,string,Guid类型作为实际类型传给泛型的类型参数时,编译器会生成3个新的类型,即TypeWithStaticField<int>,TypeWithStaticField<string>,TypeWithStaticField<Guid> 。对于编译器来说,每个封闭泛型类型都是一个不一样的类型,所以它们都有属于它自己的静态字段。对于静态方法,静态构造函数也是一样的道理。
类型参数的推断
在使用泛型时,通过编译器的类型推断,省略掉<int>这类符号,交由编译器自行推断。
namespace Generic { public class TypeInfer { public static A MyConvert<A>(object token) { if (token == null) { return default(A); } //依据A的类型,转换token的格式 return (A)Convert.ChangeType(token, typeof(A)); } public static void Exchange<T>(ref T a,ref T b) { T temp = a; a = b; b = temp; } } } int x = TypeInfer.MyConvert("12333"); //报错 int x = TypeInfer.MyConvert<int>("12333"); int a = 1,b=2; TypeInfer.Exchange(ref a, ref b);
如上例C#中编译器可以依据传入的参数的类型来推断类型参数,但不能依据返回值的类型来推断。
C#的类型推断只适用于泛型方法,不适用于泛型类型。
类型参数的约束
引用类型约束
public void Test<T>(T x) where T : class
引用类型的表示形式是 T:class, 它确保传入的类型实参必须是引用类型。当存在多个约束时,classi必须是第一位。
值类型约束
public void Test<T>(T x) where T : struct
确保传递的类型实参是值类型(包括枚举),但这里的值类型不包括可空类型。
因为T是值类型,new T()可以编译通过,因为所有值类型都有一个公共无参构造函数。
没有任何类型可以即是引用类型,也是值类型,所以 where T:class,struct 是不成立的,所以struct也和class一样在存在多个约束时,要排在第一位。
构造函数类型约束
public void Test<T>(T x) where T : new()
如果参数类型由多个约束,则此约束必须最后指定。此约束确保指定的类型实参是有公共无参构造函数的非抽象类型。
如果同时指定struct 和new()约束,编译器会报错。
转换类型约束
public void Test<T>(T x) where T : stream public void Test<T>(T x) where T : IComparable<T> public void Test<T,U>(T x) where T : U
它也可以是任何的类、接口、委托或数组等(特殊类型除外)
存在多个约束时,类约束必须放在接口约束前面。
不能指定为约束的特殊的引用类型包括:
System.Object
System.Array
System.Delegate
System.MulticastDelegate
System.ValueType
System.Enum
System.Void
当没有指定约束时,默认T是System.Object类型。 但不能在代码中显式地指定System.Object约束,否则编译器会报错。
组合约束
void Test<T, U>(T x) where T : class, U, IDisposable, new(); void Test2<T, U>(T x) where T : class where U : struct;
总结:
- class 和 struct 要排在第一位
- new()要放在最后一个
- 类约束要排在接口约束前面
- 不同的类型参数可以有不同的约束,但它们要分别有一个单独的where关键字。
默认值表达式
在泛型定义中有时是需要用到默认值的,但类型不明确,默认值也就不定。C#2提供了默认值表达式。
public static A Convert<A>(object token) { if (token == null) { return default(A); } //依据A的类型,转换token的格式 return (A)System.Convert.ChangeType(token, typeof(A)); }
与java泛型的比较
java泛型没有虚拟机级别的支持,无性能上的优化,安全性也只能在编译时予以保证。但可以向后兼容。
java泛型通过通配符支持协变性和逆变性。而C#的泛型不支持,4.0虽然引入了受限的泛型可变性,但没有java灵活。
可空类型、匿名方法、迭代器
可空类型
可空类型也是值类型,但是包含null值的值类型。
public struct Nullable<T> where T :struct
int? n = null;
? 是C#2提供的语法糖,会被编译成 Nullable<int>类型。
匿名方法
匿名方法自动形成闭包,就像其他语言中的闭包一样,会捕捉上下文变量。
对于匿名方法捕获的变量,虽然编译器会创建一个额外的类来容纳它们,这些变量会被分配在堆上。但不同于java的匿名内部类,被捕获的是原变量的引用,而不是值拷贝,所以不用定义为只读。
2.0中的委托类型支持协变性和逆变性。
引入了泛型委托类型Action<T> , Predicate<T>,Comparison<T>。
迭代器
C#1.0中使用foreach来实现访问迭代器的内置支持。一个类型要想可以使用foreach遍历,必须实现IEnumerable或IEnumerable<T>接口。接口中定义了一个返回迭代器的方法。
namespace Enumerator { public class Friend { private string name; public string Name { get { return name; } } public Friend(string name) { this.name = name; } } class Program { static void Main(string[] args) { Friends friends = new Friends(); friends.Add(new Friend("张三")); friends.Add(new Friend("李四")); friends.Add(new Friend("王五")); foreach (Friend item in friends) { Console.WriteLine(item.Name); } Console.ReadKey(); } } }
C#1.0中的迭代器实现方式
namespace Enumerator1 { //C# 1.0 的迭代器实现方式 public class Friends : IEnumerable { private ArrayList friendArray; public Friends() { friendArray = new ArrayList(); } public Friend this[int index] { get { return (Friend)(friendArray[index]); } } public int Count { get { return friendArray.Count; } } public void Add(Friend friend) { friendArray.Add(friend); } public IEnumerator GetEnumerator() { return new FriendIterator(this); } } //迭代器 public class FriendIterator : IEnumerator { private readonly Friends friends; private int index; private Friend current; internal FriendIterator(Friends friendCollection) { friends = friendCollection; index = 0; } public object Current { get { return current; } } public bool MoveNext() { if (index + 1 > friends.Count) { return false; } else { this.current = friends[index]; index++; return true; } } public void Reset() { index = 0; } } }
C# 2.0 的迭代器实现方式
namespace Enumerator2 { //C# 2.0 的迭代器实现方式 public class Friends : IEnumerable { private ArrayList friendArray; public Friends() { friendArray = new ArrayList(); } public Friend this[int index] { get { return (Friend)(friendArray[index]); } } public int Count { get { return friendArray.Count; } } public void Add(Friend friend) { friendArray.Add(friend); } public IEnumerator GetEnumerator() { for (int i = 0; i < friendArray.Count; i++) { yield return friendArray[i]; } } } }
yield return 告诉编译器这不是一个普通方法。而是一个实现迭代器块的方法。
yield return 不能在try代码块中使用。并且不能在finally块中使用yield return 或 yield break;
尽管看上去是一个顺序执行的方法。但实际上是请求编译器创建一个状态机(调用者每次只想要获取一个元素,所以在返回上一个值时需要跟踪当前的工作状态)。
使用 yield break 可以结束迭代器的执行。
尽管yield return暂时停止了方法的执行,但并没有退出方法。所以finally代码块并不会被执行。但yield break后会执行适当的finally代码块。
其他一些特性
分部类型(partial)
通过分部类型可以在多个源文件中为一个类型编写代码。特别适用于部分代码自动生成,而其他部分的代码为手写的类型。
C#3.0中分部方法提供了一些可选的钩子,这些钩子如果不去实现它们,则不会产生任何开销,任何未实现的分部方法,都会被编译器移除。C#3.0的分部方法让生成代码可以和手写代码以一种丰富的方式进行交互。
静态类
在编写工具类时,令代码更加整齐优雅。例如 System.Math类。
所有成员都是静态的(除了私有的构造函数)。
类直接从object派生。
一般情况下不应该有状态,除非涉及高速缓存或单例。
不能存在任何可见的构造函数。
类是可以密封的(让类的目标更明确)。
独立取值方法、赋值方法属性访问器
命名空间别名
::
pragma指令
pragma指令是一个由#pragma开头的代码行所表示的预处理命令。它无法将程序行为改为违法C#语言规范的任何东西,但可以实现规范之外的所有事情。如果编译器不理解某个特别的pragma指令,那么只会发出一个警告。
禁告pragma
#pragma warning disable 0169
#pragma warning restore 0169
非安全代码中固定大小的缓冲区fixed
友元程序集
C#3.0
自动实现的属性
可以彻底隐藏了相应的字段,保障了内外访问的一致性。
隐式类型var
对象初始化器
public class HomePlace { public string Country { get; set; } public string Town { get; set; } }
public class Person { public string Name { get; set; } public int Age { get; set; } public HomePlace Home; public Person(string name) { Name = name; } } Person tom = new Person("Tom") { Age = 9, Home = new HomePlace{ Country ="UK",Town="Reading"} };
public class Person { public string Name { get; set; } public int Age { get; set; } public HomePlace Home; public Person(string name) { Name = name; Home = new HomePlace(); } } Person tom = new Person("Tom") { Age = 9, Home = { Country ="UK",Town="Reading"} };
对象集合初始化器
Person[] family = new Person[] { new Person("Holly"), new Person("Jon") { Age=36}, new Person("Tom") { Age = 9, Home = { Country ="UK",Town="Reading"} } };
匿名类型
通过对象初始化器和隐式类型创建一个未知类型的对象。使我们在不定义类型的情况下可以实现对象的创建。
Lambda表达式
泛型委托类型
.Net3.5中提供了17个有返回值的泛型Func委托类型。
TResult Func<TResult>()
TResult Func<T,TResult>(T arg)
……
从无参数,到16个参数共计17个定义
.Net2.0中提供的17个无返回值的泛型Action委托类型。
lambda表达式
x=>x.Name
(a,b)=>{
return a + b;
}
lambda表达式是匿名方法的替代,代码简洁优雅得多。而从格式上也和其他语言的lambda表达式差不多。用一种更简便的方式创建委托实例,并向更函数化的开发模式转移。
表达式树
.Net3.5提供的表达式树(System.Linq.Expressions)以一种抽象的方式将一些代码表示成一个对象树,它类似于CodeDOM,但在更高的级别上操作。表达式树主要用于LINQ。顾名思义,它们是对象构成的树,树中每一个节点本身都是一个表达式。
//System.Linq.Expressions下包含一系列表达式类型,它们都继承自Expression //Expression是一个抽象的主要包含一个静态工厂方法的类 Expression firstArg = Expression.Constant(2); //ConstantExpression Expression secondArg = Expression.Constant(3); Expression add = Expression.Add(firstArg, secondArg); //BinaryExpression Console.WriteLine(add);//(2 + 3) add = Expression.Subtract(add, Expression.Constant(3)); Console.WriteLine(add);//((2 + 3) - 3) //Expression<TDelegate> => LambdaExpression => Expression,TDelegate为委托类型 Expression<Func<int>> result = Expression.Lambda<Func<int>>(add); Func<int> compiled = result.Compile(); Console.WriteLine(compiled());//2
C#3对于将Lambda表达式转换为表达式树提供了内建的支持。
Expression<Func<int>> result = () => 2+3-3; // 报错:无法将具有语句体的lambda表达式转换为表达式树 //Expression<Func<int>> result = () => { return 2 + 3}; Func<int> compiled = result.Compile(); Console.WriteLine(compiled());//2
表达式树和Lambda表达式的链接
一个更复杂点的表达式树
public static void test2() { Expression<Func<string, string, bool>> lambda = (x, y) => x.StartsWith(y); var compiled = lambda.Compile(); Console.WriteLine(compiled("First", "Second"));//false Console.WriteLine(compiled("First", "Fir"));//true }
用代码构建一个方法调用表达式树
public static void test() { //定义表达式树中要用到的参数 var target = Expression.Parameter(typeof(string), "x"); var methodArg = Expression.Parameter(typeof(string), "y"); //表达式树中要调用的方法 MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) }); //调用方法的表达式 Expression[] methodArgs = new[] { methodArg }; Expression call = Expression.Call(target, method, methodArgs); //将call转换为lambda表达式 var lambdaParameters = new[] { target, methodArg }; var lambda = Expression.Lambda<Func<string, string, bool>>(call, lambdaParameters); var compiled = lambda.Compile(); Console.WriteLine(compiled("First", "Second"));//false Console.WriteLine(compiled("First", "Fir"));//true }
另一个实现(通过方法名,执行Controller里的Action)
public class ControllerActionInvoker : IActionInvoker { public IModelBinder ModelBinder { get; private set; } public ControllerActionInvoker() { this.ModelBinder = new DefaultModelBinder(); } //表达式树的调用 public void InvokeAction(ControllerContext controllerContext, string actionName) { MethodInfo methodInfo = controllerContext.Controller.GetType().GetMethods().First(m => string.Compare(actionName, m.Name, true) == 0); List<object> parameters = new List<object>(); foreach (ParameterInfo parameter in methodInfo.GetParameters()) { parameters.Add(this.ModelBinder.BindModel(controllerContext, parameter.Name, parameter.ParameterType)); } ActionExecutor executor = new ActionExecutor(methodInfo); ActionResult actionResult = (ActionResult)executor.Execute(controllerContext.Controller, parameters.ToArray()); actionResult.ExecuteResult(controllerContext); } } internal class ActionExecutor { private static Dictionary<MethodInfo, Func<object, object[], object>> executors = new Dictionary<MethodInfo, Func<object, object[], object>>(); private static object syncHelper = new object(); public MethodInfo MethodInfo { get; private set; } public ActionExecutor(MethodInfo methodInfo) { this.MethodInfo = methodInfo; } //表达式树的执行 public object Execute(object target, object[] arguments) { Func<object, object[], object> executor; if (!executors.TryGetValue(this.MethodInfo, out executor)) { lock (syncHelper) { if (!executors.TryGetValue(this.MethodInfo, out executor)) { executor = CreateExecutor(this.MethodInfo); executors[this.MethodInfo] = executor; } } } return executor(target, arguments); } //表达式树的生成 private static Func<object, object[], object> CreateExecutor(MethodInfo methodInfo) { ParameterExpression target = Expression.Parameter(typeof(object), "target"); ParameterExpression arguments = Expression.Parameter(typeof(object[]), "arguments"); List<Expression> parameters = new List<Expression>(); ParameterInfo[] paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) { ParameterInfo paramInfo = paramInfos[i]; BinaryExpression getElementByIndex = Expression.ArrayIndex(arguments, Expression.Constant(i)); UnaryExpression convertToParameterType = Expression.Convert(getElementByIndex, paramInfo.ParameterType); parameters.Add(convertToParameterType); } UnaryExpression instanceCast = Expression.Convert(target, methodInfo.ReflectedType); MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameters); UnaryExpression convertToObjectType = Expression.Convert(methodCall, typeof(object)); return Expression.Lambda<Func<object, object[], object>>(convertToObjectType, target, arguments).Compile(); } } public class RawContentResult : ActionResult { public Action<TextWriter> Callback { get; private set; } public RawContentResult(Action<TextWriter> action) { this.Callback = action; } public override void ExecuteResult(ControllerContext context) { this.Callback(context.RequestContext.HttpContext.Response.Output); } }
位于LINQ核心的表达式树
没有lambda表达式,表达式树几乎没有任何价值。而如果只想构建单个表达式而不是完整的语句、方法、类型等,它们可作为CodeDOM的替代方案,但价值有限。
LINQ就是Lambda表达式、表达式树和扩展方法的联合应用。
要指示另一平台运行一些代码,这些指示一般表示为文本(如SQL查询),但无法在编译时检查。 表达式树可以将执行模型从所需的逻辑中提取出来,而lambda表达式则提供了编译时检查的能力。“进程外”LINQ提供器的中心思想在于,我们可以从一个熟悉的源语言(C#)生成一个表达式树,将结果作为一个中间格式,再将其转换成目标平台的本地语音,如SQL。或者是用于调用本机API,这些API根据表达式所表示的内容来调用不同的WEB服务。
PS:
编译器并不能做所有检查。表达式树确保了大量的编译时安全性,但编译器只能检查以确保Lambda表达式能转换成一个有效的表达式树。但它不能保证表达式树最后的使用是否合适。
LINQ之外的表达式树
尽管表达式树是为LINQ而引入.NET的,但它还有一些其他的用法。
扩展方法
既有静态方法的优点,又使调用它们的代码的可读性得到了提高。
扩展方法的使用
- 必须是一个静态方法
- 至少要有一个参数
- 第一个参数必须附加this 关键字作为前缀
- 第一个参数不能有其他任何修饰符(out或ref)
- 第一个参数不能是指针类型。
第一个参数的类型称为扩展类型。
public static class StreamUtil { const int BufferSize = 8192; public static void CopyTo(this Stream input,Stream output) { byte[] buffer = new byte[BufferSize]; int read; while ((read = input.Read(buffer,0,BufferSize))>0) { output.Write(buffer, 0, read); } } public static byte[] ReadFully(this Stream input) { using (MemoryStream tempStream = new MemoryStream()) { input.CopyTo(tempStream); return tempStream.ToArray(); } } } class Program { static void Main(string[] args) { WebRequest request = WebRequest.Create("http://www.baidu.com"); using (WebResponse respone = request.GetResponse()) using (Stream responeStream = respone.GetResponseStream()) using (FileStream output = File.Create("C:\\新建文件夹\\1.html")) { responeStream.CopyTo(output); } } }
如果存在适当的实例方法,则实例方法肯定会先于扩展方法使用。而编译器也不会给出警告。
在.net4中Stream类新引入了一个方法CopyTo,它的其中一个重载的签名和上例中的扩展方法签名一样。结果就是在4.0以上的环境下执行的是实例方法,而不是扩展方法。