最近发现对于C#的使用水平一只停留在3.0的程度 对于4.0 5.0的新特性使用的很少,写一篇文章记录一下增加一下认识。
C# 3.5
扩展方法
扩展方法所在的类和扩展方法必须是静态的 并且扩展方法第一个参数是要扩展的类名 并在this
Person person = new Person();
public static class Extension
{
public static void ExtensionMethod(this Person p)
{
.......;
}
}
person.ExtensionMethod();
当person调用 ExtensionMethod()时,C# Compiler 会把这段Source Code 编译成Static Method 的IL Code。也可以理解为
编译器做了这样的处理,person.ExtensionMethod(); => ExtensionMethod(person);
C# Compiler 编译的过程是这样的:
- 首先查看Person类中是否有ExtensionMethod()方法,有的话,直接调用;
- 如果没有,在相应的NameSpace 当中,查找static Class,看看这些static Class当中,是否有public static void ExtensionMethod(this Person p) 这样的方法,如果也没有,编译通不过。
c#4.0
弱引用
我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的。我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET中提供了WeakReference来实现。弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。弱引用使用起来很简单,看下面的代码:
Object obj = new Object();
WeakReference wref = new WeakReference( obj );
obj = null;
第一行代码新建了一个新的对象,这里叫它对象A,obj是对对象A的强引用。接着第二行代码新建了一个弱引用对象,参数就是对象A的强引用,第三行代码释放掉对对象A的强引用。这时如果GC进行回收,对象A就会被回收。
怎样在取得对象A的强引用呢?很简单,请看代码2:
Object obj2 = wref.Target;
if( obj2 != null )
{
// 做你想做的事吧。
}
else
{
// 对象已经被回收,如果要用必须新建一个。
}
只要显示的将弱引用的Target属性附值就会得到弱引用所代表对象的一个强引用。不过在使用对象之前要对其可用性进行检查,因为它可能已经被回收了。如 果你得到的是null(VB.NET下为Nothing),表明对象已经被回收,不能再用了,需要重新分配一个。如果不是null,就可以放心大胆的用 了。
接下来让我们看WeakReference的另外一个版本,请看代码3:
// public WeakReference(
// object target,
// bool trackResurrection
//);
Object obj1 = new Object();
Object obj2 = new Object();
WeakReference wref1 = new WeakReference( obj1, false );
WeakReference wref2 = new WeakReference( obj2, true );
WeakReference的另外一个版本有两个参数,第一个参数和我们前面用的版本的一样。第二个参数让我们看一下他的原型,bool trackResurrection,跟踪复活,是个bool型,就是是否跟踪复活。前面的文章中我提到过需要Finalize的对象在最终释放前会有一 次复活,我们大概可以猜到第二个参数表示的意思了。如果我们第二个参数给false,这个弱引用就是一个short weak reference(短弱引用),当GC回收时,发现根中没有这个对象的引用了,就认为这个对象无用,这时短弱引用对这个对象的跟踪到此为止,弱引用的 Target被设置为null。前面的一个参数的构造函数版本新建的弱引用为短弱引用。如果第二个参数给true,这个弱引用就是一个long weak reference(长弱引用)。在对象的Finalize方法没有被执行以前,Target都可用。不过这是对象的某些成员变量也许已经被回收,所以使 用起来要想当小心。
现在让我们看看WeakReference是如何实现的。很显然WeakReference不能直接的引用目标对象,WeakReference的 Target属性的get/set是两个函数,从某处查到目标对象的引用返回,而不是我们最常用写的那样直接返回或者设置一个私有变量。GC维护了两个列 表来跟踪两种弱引用的目标对象,在一个 WeakReference对象创建时,它在相应的列表中找到一个位置,将目标对象的引用放入,很显然,这两个列表不是根的一部分。在GC进行内存回收的 时候,如果要回收某一个对象,会检查弱引用的列表,如果保存着这个对象的引用,则将其设为null。
可选参数方法
带有可选参数方法的声明:
public StreamReader OpenTextFile(string path,Encoding encoding = null,bool detectEncoding = true,int bufferSize = 1024);
命名参数必须在最后使用:
OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);
顺序不限:
OpenTextFile(bufferSize: 4096, path: "foo.txt", detectEncoding: false);
Lazy<T>
我们也许会遇到这样一种情况,我们有一个大家伙(大对象)需要创建,那么这个对象的创建时需要较长的时间,同时也需要在托管堆上分配较多的空间。
那么在.NET Framework 4 中提供了这样一个很聪明的方式:Lazy<T>(我们可以称之为懒对象)。当然,在之前,很多人也曾对其进行过自己的实现。
那么我们在这里就可以把 Lazy<T> 的作用总结为一句话,按需延迟加载。
内存映射文件
<?XML:NAMESPACE PREFIX = [default] http://www.w3.org/1999/xhtml NS = "http://www.w3.org/1999/xhtml" />利用文件与内存空间之间的映射,应用程序(包括多个进程)可以通过直接在内存中进行读写来修改文件。 从 .NET Framework 4 版开始,可以使用托管代码按照本机 Windows 函数访问内存映射文件的方式来访问内存映射文件,如 MSDN Library 中的 Managing Memory-Mapped Files in Win32(管理 Win32 中的内存映射文件)中所述。
动态语言运行时
4.0中增加了与编译器相关的API,这样就可以将字符串作为代码动态编译执行,跟javascript好像。借助于 DLR,可以更轻松地开发要在 .NET Framework 上运行的动态语言,而且向静态类型化语言添加动态功能也会更容易。
动态语言可以在运行时标识对象的类型,而在类似 C# 和 Visual Basic 的静态类型化语言中(当您使用 Option Explicit On 时),您必须在设计时指定对象类型。 动态语言的示例有:Lisp、Smalltalk、JavaScript、PHP、Ruby、Python、ColdFusion、Lua、Cobra 和 Groovy。
大多数动态语言都会向开发人员提供以下优点:
-
可以使用快速反馈循环(REPL 或读取-计算-打印循环)。 这样,您就可以在输入几条语句之后立即执行它们以查看结果。
-
同时支持自上而下的开发和更传统的自下而上的开发。 例如,当您使用自上而下的方法时,可以调用尚未实现的函数,然后在需要时添加基础实现。
-
更易于进行重构和代码修改操作,原因是您不必在代码中四处更改静态类型声明。
利用动态语言可以生成优秀的脚本语言。 利用新的命令和功能,客户可以轻松地扩展使用动态语言创建的应用程序。 动态语言还经常用于创建网站和测试工具、维护服务器场、开发各种实用工具以及执行数据转换。
DLR 的目的是允许动态语言系统在 .NET Framework 上运行,并为动态语言提供 .NET 互操作性。 在 Visual Studio 2010 中,DLR 将动态对象引入到 C# 和 Visual Basic 中,以便这些语言能够支持动态行为,并且可以与动态语言进行互操作。
DLR 还可帮助您创建支持动态操作的库。 例如,如果您具有一个使用 XML 或 JavaScript 对象表示法 (JSON) 对象的库,则对于使用 DLR 的语言,您的对象可以显示为动态对象。 这使库用户能够编写语法更简单且更自然的代码,以便操作对象和访问对象成员。
例如,在 C# 中,您可能会使用下面的代码来递增 XML 中的计数器值。
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
通过使用 DLR,您可以改用下面的代码来执行相同的操作。
scriptobj.Count += 1;
与 CLR 类似,DLR 是 .NET Framework 的一部分,并随 .NET Framework 和 Visual Studio 安装包一起提供。 DLR 的开放源代码版本还可以从 CodePlex 网站下载获得。
泛型中的协变和逆变
在C#中,下面的类型转换是非法的:
IList<string> strings = new List<string>();
IList<object> objects = strings;
因为你有可能会这样做,而编译器的静态检查无法查出错误:
objects[0] = 5;
string s = strings[0];
4.0中在声明generic的Interface及Delegate时可以加in及out关键字,如:
public interface IEnumerable<out T> : IEnumerable{IEnumerator<T> GetEnumerator();}public interface IEnumerator<out T> : IEnumerator{bool MoveNext();T Current { get; }}
public interface IComparer<in T>{public int Compare(T left, T right);}
out关键字的意思是说IEnumerable<T>中T只会被用在输出中,值不会被改变。这样将IEnumerable<string>转为IEnumerable<object>类型就是安全的。
in的意思正好相反,是说IComparer<T>中的T只会被用在输入中,这样就可以将IComparer<object>安全的转为IComparer<string>类型。
前者被称为Co-Variance, 后者就是Contra-Variance。
.Net4.0中使用out/in声明的Interface:
System.Collections.Generic.IEnumerable<out T>System.Collections.Generic.IEnumerator<out T>System.Linq.IQueryable<out T>System.Collections.Generic.IComparer<in T>System.Collections.Generic.IEqualityComparer<in T>System.IComparable<in T>
Delegate:
System.Func<in T, …, out R>System.Action<in T, …>System.Predicate<in T>System.Comparison<in T>System.EventHandler<in T>
C# 5.0
异步文件 I/O
// Three things to note in the signature: // - The method has an async modifier. // - The return type is Task or Task<T>. (See "Return Types" section.) // Here, it is Task<int> because the return statement returns an integer. // - The method name ends in "Async." async Task<int> AccessTheWebAsync() { // You need to add a reference to System.Net.Http to declare client. HttpClient client = new HttpClient(); // GetStringAsync returns a Task<string>. That means that when you await the // task you'll get a string (urlContents). Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // You can do work here that doesn't rely on the string from GetStringAsync. DoIndependentWork(); // The await operator suspends AccessTheWebAsync. // - AccessTheWebAsync can't continue until getStringTask is complete. // - Meanwhile, control returns to the caller of AccessTheWebAsync. // - Control resumes here when getStringTask is complete. // - The await operator then retrieves the string result from getStringTask. string urlContents = await getStringTask; // The return statement specifies an integer result. // Any methods that are awaiting AccessTheWebAsync retrieve the length value. return urlContents.Length; }
如果 AccessTheWebAsync 没有它可以完成调用 GetStringAsync 和等待计算完成之间的任何工作,通过调用和等待简化代码在下面的单个语句。
string urlContents = await client.GetStringAsync();
-
方法签名包含一个 Async 或 async 修饰符。
-
异步方法的名称以“Async”后缀,按照约定,关闭。
-
返回类型为下列类型之一:
-
Task<TResult> ,如果您的方法具有操作个线程类型 TResult 的返回语句。
-
Task ,如果方法没有返回语句或具有返回语句不操作。
-
无效 (在 Visual Basic 中 子 ),如果您编写一个异步事件处理程序。
有关更多信息,请参见“之后返回类型和参数”本主题。
-
-
方法通常包含至少一个等待表达式,指示个方法无法继续,直到该等待的异步操作完成的。 同时,方法被挂起,并且,控件返回到方法的调用方。 本主题的下一节将解释发生的悬挂点。
在异步方法,可以使用提供的关键字和类型指示要执行,因此,编译器执行方式,包括记录必须出现,当控件处于挂起的方法时回时间点。 某些实例处理,例如循环,而异常处理,可能很难进行在传统异步代码的句柄。 在异步方法,解决您编写这些元素,因为在一个同步解决方案会并将问题。
调用过程:
在关系图的数值对应于以下步骤。
-
<?XML:NAMESPACE PREFIX = [default] http://www.w3.org/1999/xhtml NS = "http://www.w3.org/1999/xhtml" />事件处理程序调用并等待 AccessTheWebAsync 异步方法。
-
AccessTheWebAsync 创建 HttpClient 实例并调用 GetStringAsync 异步方法下载网站内容作为字符串。
-
挂起团队进度的内容发生 GetStringAsync 发生。 可能必须等待网站下载或一些其他块的事件。 若要避免妨碍资源,GetStringAsync 为控件对其调用方,AccessTheWebAsync。
GetStringAsync 返回 TResult 是字符串的 Task<TResult>,并且,AccessTheWebAsync 将任务指派给 getStringTask 变量。 在工作完成时,任务表示继续对定向到 GetStringAsync,以提交导致实际字符串值。
-
由于 getStringTask 不等待,AccessTheWebAsync 可以继续执行不依赖于从 GetStringAsync的最终结果的其他工作。 该任务由为同步方法DoIndependentWork的调用表示。
-
DoIndependentWork 完成其工作并回调用方的同步方法。
-
AccessTheWebAsync 用完了它可以完成,不会受到 getStringTask的结果的工作。 接下来AccessTheWebAsync 若要计算并返回该下载的字符串的长度,但是,该方法无法计算该值,直到该方法具有字符串。
因此,AccessTheWebAsync 使用一个等待运算符挂起的进度并使控件到调用 AccessTheWebAsync的方法。 AccessTheWebAsync 返回 Task(Of Integer) 或 Task<int> 调用方。 任务表示形式导致为下载的字符串的长度的整数结果。
说明
如果 GetStringAsync (并 getStringTask) 后,在 AccessTheWebAsync 等待之前,控件在 AccessTheWebAsync保持。 成本挂起然后返回到AccessTheWebAsync 将浪费,如果异步调用过程 (getStringTask) 已经完成了,并且 AccessTheWebSync 不必等待最终结果。
在调用方 (在此示例中的事件处理程序内),处理重复。 调用方可能完成不依赖于从 AccessTheWebAsync 的结果在等待该结果之前的其他工作,或调用方可能立即等待。 在事件处理程序到达等待表达式时,应用程序集中精力 GetStringAsync的完成。 事件处理程序等待 AccessTheWebAsync,并且,AccessTheWebAsync 等待 GetStringAsync。
-
GetStringAsync 完成并生成一个字符串结果。 字符串结果不通过对 GetStringAsync 的调用返回方式与您可能期望的。 (请确保方法返回已在第 3 步 .) 的任务,字符串结果在表示方法的完成的任务,getStringTask存储。 等待运算符从 getStringTask检索结果。 赋值语句将检索的结果赋给urlContents。
-
当 AccessTheWebAsync 具有字符串结果时,方法可以计算该字符串的长度。 然后 AccessTheWebAsync 工作也完成的,因此,等待事件处理程序可以继续。 在完整的示例中的主题末尾,可以确认事件处理程序检索和打印长度结果的值。
如果您不熟悉异步编程,请需要一分钟考虑同步和异步行为之间的差异。 一个同步方法返回,其工作完成 (第 5 步),但是,异步方法返回任务值,其工作挂起时 (第 3 步和第 6 步)。 在异步方法最终完成其工作时,任务将会标记为已完成和结果,如果有,在任务中。
在什么情况下可能想知道找到支持异步编程的方法 (如 GetStringAsync。 .NET Framework 4.5 包含与异步以及等待的许多成员。 您可以通过附加到成员名称和 Task 或 Task<TResult>的返回类型“Async”后缀识别这些成员。 例如,System.IO.Stream 选件类包含方法例如 CopyToAsync、ReadAsync和 WriteAsync 在同步方法 CopyTo、Read和 Write。
异步方法旨在成为非阻塞操作。 当等待的任务运行时,在异步方法的一个等待表达式不会阻止当前线程。 相反,该表达式注册该方法的其余部分作为继续并返回控制对异步方法的调用方。
异步和等待关键字不会导致其他线程创建。 因为异步方法本身并不会运行的线程,异步方法不需要多线程。 只有 + 当方法处于活动状态,则方法在当前同步上下文中运行并使用在线程的时间。 可以使用 Task.Run 移动 CPU 工作移到后台线程,但是,后台线程不利于等待结果变得可用处理。
以异步编程的基于异步的方法优于于几乎每个用例的现有方法。 具体而言,此方法比 IO 操作的 BackgroundWorker 好,因为代码更为简单的,因此无需防止争用条件。 与 Task.Run的组合,异步编程的 CPU 操作的 BackgroundWorker 好,因为异步编程从 Task.Run 传输到线程池的工作分隔运行您的代码以协调详细信息。
如果指定使用 异步 或 异步 修饰符,方法是异步方法,可以实现以下两个函数。
-
清单异步方法可以使用 Await 或指定的 等待 悬挂点。 等待运算符通知编译器异步方法不能继续点的过去,直到等待的异步过程完成。 同时,控制权交还异步方法的调用方。
一个异步方法的备用在等待表达式的不构成从方法的退出,并且,finally 块不会运行。
-
清单异步方法本身可以通过调用它的方法等待。
异步方法通常包含等待运算符的一个或多个匹配项,但是,请假等待表达式不会导致编译器错误。 如果异步方法不会将等待运算符指示悬挂点,方法尽管"修饰符执行,一个同步方法。 编译器会发出此类方法的警告。
Async 、async、Await和 await 是上下文关键字。 有关更多信息和示例,请参见以下主题:
在编程 .NET framework,异步方法通常返回 Task 或 Task<TResult>。 在异步方法中,等待运算符应用于从调用返回到另一个异步方法的任务。
您指定 Task<TResult>,因为返回类型,则方法包含指定类型 TResult操作上的一个 返回 (Visual Basic) 或 返回 (c#) 语句。
使用 Task,因为返回类型,则该方法没有返回语句或具有不返回操作线程的 return 语句。
下面的示例演示如何声明并调用返回 Task<TResult> 或 Task的方法。
// Signature specifies Task<TResult> async Task<int> TaskOfTResult_MethodAsync() { int hours; // . . . // Return statement specifies an integer result. return hours; } // Calls to TaskOfTResult_MethodAsync Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync(); int intResult = await returnedTaskTResult; // or, in a single statement int intResult = await TaskOfTResult_MethodAsync(); // Signature specifies Task async Task Task_MethodAsync() { // . . . // The method has no return statement. } // Calls to Task_MethodAsync Task returnedTask = Task_MethodAsync(); await returnedTask; // or, in a single statement await Task_MethodAsync();
每个返回的任务表示正在进行的工作。 任务封装有关状态的信息异步过程和,最后,从进程的最终结果或处理引发的异常;如果未成功。
异步方法也是 Sub 方法 (Visual Basic) 或使 void 返回类型 (c# 中)。 这将返回类型主要用于定义事件处理程序,void 返回类型需要。 异步事件处理程序通常用作异步程序的起始点。
是 Sub 程序或具有 void 返回类型不能等待的异步方法和一个无效返回的方法的调用方无法捕获方法引发的任何异常。
异步方法不能声明在 Visual Basic 或 ref 的 ByRef 参数或在 C# 中 http://msdn.microsoft.com/zh-cn/library/t3c3bfhx.aspx 参数,但是,方法可以调用具有这些参数的方法。
有关更多信息和示例,请参见异步返回类型(C# 和 Visual Basic)。 有关如何捕获异步方法的异常的更多信息,请参见 try-catch(C# 参考) 或Try...Catch...Finally 语句 (Visual Basic)。
在 Windows 运行时 编程的异步 API 使之一返回类型,类似于任务:
-
IAsyncAction,对应于 Task
有关更多信息和示例,请参见 快速入门:使用异步编程的时间运算符。
按照约定,您追加“Async”传递给具有 Async 或 async 修饰符方法的名称。
您可以忽略事件、基类或接口协定建议一个不同的名称约定。 例如,您不应向常用事件处理程序重命名,例如 Button1_Click。
弱事件