C# 5.0随着VisualStudio 2012一起正式发布了,让我们来看看C#5.0中增加了哪些功能。

1. 异步编程

在.Net 4.5中,通过async和await两个关键字,引入了一种新的基于任务的异步编程模型(TAP)。在这种方式下,可以通过类似同步方式编写异步代码,极大简化了异步编程模型。如下式一个简单的实例:

    static async void DownloadStringAsync2(Uri uri)
{
var webClient = new WebClient();
var result = await webClient.DownloadStringTaskAsync(uri);
Console.WriteLine(result);
}

而之前的方式是这样的:

    static void DownloadStringAsync(Uri uri)
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (s, e) =>
{
Console.WriteLine(e.Result);
};
webClient.DownloadStringAsync(uri);
}

也许前面这个例子不足以体现async和await带来的优越性,下面这个例子就明显多了:

  public void CopyToAsyncTheHardWay(Stream source, Stream destination)
{
byte[] buffer = new byte[0x1000];
Action<IAsyncResult> readWriteLoop = null;
readWriteLoop = iar =>
{
for (bool isRead = (iar == null); ; isRead = !isRead)
{
switch (isRead)
{
case true:
iar = source.BeginRead(buffer, , buffer.Length,
readResult =>
{
if (readResult.CompletedSynchronously) return;
readWriteLoop(readResult);
}, null);
if (!iar.CompletedSynchronously) return;
break;
case false:
int numRead = source.EndRead(iar);
if (numRead == )
{
return;
}
iar = destination.BeginWrite(buffer, , numRead,
writeResult =>
{
if (writeResult.CompletedSynchronously) return;
destination.EndWrite(writeResult);
readWriteLoop(null);
}, null);
if (!iar.CompletedSynchronously) return;
destination.EndWrite(iar);
break;
}
}
};
readWriteLoop(null);
} public async Task CopyToAsync(Stream source, Stream destination)
{
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await source.ReadAsync(buffer, , buffer.Length)) != )
{
await destination.WriteAsync(buffer, , numRead);
}
}

关于基于任务的异步编程模型需要介绍的地方还比较多,不是一两句能说完的,有空的话后面再专门写篇文章来详细介绍下。另外也可参看微软的官方网站:Visual Studio Asynchronous Programming,其官方文档Task-Based Asynchronous Pattern Overview介绍的非常详细, VisualStudio中自带的CSharp Language Specification中也有一些说明。

2. 调用方信息

很多时候,我们需要在运行过程中记录一些调测的日志信息,如下所示:

    public void DoProcessing()
{
TraceMessage("Something happened.");
}

为了调测方便,除了事件信息外,我们往往还需要知道发生该事件的代码位置以及调用栈信息。在C++中,我们可以通过定义一个宏,然后再宏中通过__FILE__和__LINE__来获取当前代码的位置,但C#并不支持宏,往往只能通过StackTrace来实现这一功能,但StackTrace却有不是很靠谱,常常获取不了我们所要的结果。

针对这个问题,在.Net 4.5中引入了三个Attribute:CallerMemberName、CallerFilePath和CallerLineNumber。在编译器的配合下,分别可以获取到调用函数(准确讲应该是成员)名称,调用文件及调用行号。上面的TraceMessage函数可以实现如下:

  public void TraceMessage(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = )
{
Trace.WriteLine("message: " + message);
Trace.WriteLine("member name: " + memberName);
Trace.WriteLine("source file path: " + sourceFilePath);
Trace.WriteLine("source line number: " + sourceLineNumber);
}

另外,在构造函数,析构函数、属性等特殊的地方调用CallerMemberName属性所标记的函数时,获取的值有所不同,其取值如下表所示:

调用的地方

CallerMemberName获取的结果

方法、属性或事件

方法,属性或事件的名称

构造函数

字符串 ".ctor"

静态构造函数

字符串 ".cctor"

析构函数

该字符串 "Finalize"

用户定义的运算符或转换

生成的名称成员,例如, "op_Addition"。

特性构造函数

特性所应用的成员的名称

例如,对于在属性中调用CallerMemberName所标记的函数即可获取属性名称,通过这种方式可以简化 INotifyPropertyChanged 接口的实现。关于调用方信息更详细的资料,请参看MSDN:http://msdn.microsoft.com/zh-cn/library/hh534540.aspx

方法调用信息

这是一个被写在Writting Enterprisey Code上的完整风格指南,但是其中我最喜欢的是迷人对你调用过的所有函数的日志记录:

Function AddTwoNumbers(a As Integer, b As Integer) As Integer
Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Entering AddTwoNumbers")
Dim result = OracleHelpers.ExecInteger("SELECT " & a & " + " & b)
Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Calling PrintPurchaseOrders")
PrintPurchaseOrders() ' IFT 12.11.96: don't know why this is needed but shipping module crashes if it is removed
Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Returned from PrintPurchaseOrders")
Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Exiting AddTwoNumbers")
Return result
End Function

即使这段代码用企业级标准编写得有效而清晰,而使用C# 5可以使它更加高效和清晰。C# 4推荐使用可选的参数,这意味着方法调用者可以不用考虑参数,编译器将会用默认值填充。

public void WonderMethod(int a = , string b = "hello") { ... }

WonderMethod();  // compiles to WonderMethod(456, "hello")
WonderMethod(); // compiles to WonderMethod(123, "hello")

有了C# 5,你可以将一个特殊属性放置在可选参数上,编译器将会使用调用方法的信息填充变量而不是使用某个常量。这意味着我们能够实现Logger.Trace,来自动收集它是从哪里调用的信息:

public static void Trace(string message, [CallerFilePath] string sourceFile = "", [CallerMemberName] string memberName = "") {
string msg = String.Format("{0}: {1}.{2}: {3}",
DateTime.Now.ToString("yyyy-mm-dd HH:MM:ss.fff"), // Lurking 'minutes'/'months' bug introduced during .NET port in 2003 and has not been noticed because nobody ever looks at the log files because they contain too much useless detail
Path.GetFileNameWithoutExtension(sourceFile),
memberName,
message);
LoggingInfrastructure.Log(msg);
}

现在,如果调用LogTrace("some message"),编译器将会不会用空字符串填充而是使用文件以及调用所发生的成员:

// In file Validation.cs
public void ValidateDatabase() {
Log.Trace("Entering method");
// compiles to Log.Trace("Entering method", "Validation.cs", "ValidateDatabase")
Log.Trace("Exiting method");
}

请注意这些你为参数设置的属性必须是可选的,如果不是可选的,C#编译器将需要调用代码主动提供,并且提供的值必须覆盖默认值。

另一个你怎样使用这个的例子便是实现INotifyPropertyChanged,不需要逐字的匹配字符串,表达式:

public class ViewModelBase : INotifyPropertyChanged {
protected void Set<T>(ref T field, T value, [CallerMemberName] string propertyName = "") {
if (!Object.Equals(field, value)) {
field = value;
OnPropertyChanged(propertyName);
}
}
// usual INPC boilerplate
} public class Widget : ViewModelBase {
private int _sprocketSize;
public int SprocketSize {
get { return _sprocketSize; }
set { Set(ref _sprocketSize, value); } // Compiler fills in "SprocketSize" as propertyName
}
}

很值得的是,你也可以使用[CallerLineNumber]得到调用代码的行号,这个也许对诊断方法有用,但是如果你真的需要它,这也许是这段调用代码太过“企业化”的迹象。

在lambdas中使用循环变量

技术上,这个对长期存在的困扰和煎熬的修正,但是使得C#增加了可用性,所以我将会提及它。

自从C# 3以来,编写匿名函数比命名的更见快捷和容易了,匿名函数被广泛地使用鱼LINQ,但是他们也在其他情况下被使用,如你想要在不需要授权的巨大层级类和接口以及可见函数中快速的拥有参数化行为。匿名函数的一个重要特性就是你可以从本地环境中捕获变量,以下是一个示例:

public static IEnumerable<int> GetGreaterThan(IEnumerable<int> source, int n) {
return source.Where(i => i > n);
}

看这里,i=>i>n是一个捕获了n值的匿名函数。例如如果n是17,那么该函数便是 i=>i>17

在C#之前的版本中,如果你编写了一个循环,你不能在lambda中使用循环变量。事实上,它比想象中更糟。你可以在lambda中使用循环变量,但是它将给你一个错误的结果。它会使用循环退出时的玄幻变量值,不是呗捕获时的值。

例如,下面是一个返回“加法器”集合的函数:

public static List<Func<int, int>> GetAdders(params int[] addends) {
var funcs = new List<Func<int, int>>();
foreach (int addend in addends) {
funcs.Add(i => i + addend);
}
return funcs;
}
var adders = GetAdders(, , , , );
foreach (var adder in adders) {
Console.WriteLine(adder());
}

很明显这大错特错!在返回的集合中的每个函数都在捕获5作为加数后结束。这是因为为他们结束在循环变量,加数,然后最终的循环变量值为5。

要想在C# 3和4中使用这些,你需要记住将循环变量拷贝至一个局部变量中,然后用你的lambda覆盖局部变量:

foreach (var addend_ in addends) {
var addend = addend_; // DON'T GO NEAR THE LOOP VARIABLE
funcs.Add(i => i + addend)
}

由于这些函数是被局部变量覆盖而不是用循环变量,这些值现在被保存,你便能获得真确的值。

以此种方式并不是一种模糊的边缘情况,我在我的项目中碰到过很多次。有一个来自某个项目中的更加现实的例子便是构建一个用来过滤的函数,这个函数是构建自被用户指定的约束对象集合。该代码循环处理约束对象,并构建代表子句的函数列表(如 Name Equals "BOB" 变成 r =>r["Name"]=="BOB"),然后将这些函数混合至一个最终的过滤器中,该过滤器运行这所有的子句然后检查他们是不是为真。我第一次运行没有成功因为每隔子句函数以相同的约束对象覆盖--集合中的最后一个。

在C# 5中,这些修复以及你可以覆盖的循环变量能使你获得你期望的结果。

via:mindscapehq.com , OSChina原创编译

C# 5.0中新增特性的更多相关文章

  1. Performance Tuning guide 翻译 || Performance Tuning Guide 11G中新增特性

    CSDN 对格式支持比較弱.能够到http://user.qzone.qq.com/88285879/blog/1399382878 看一致的内容. Performance Tuning Guide  ...

  2. 第30月第11天 Xcode 9.0中新增的API版本检查@available

    1.Xcode 9.0中新增的API版本检查@available https://www.jianshu.com/p/0a94baa6c3dd https://www.jianshu.com/p/b8 ...

  3. hive 0.10 0.11新增特性综述

    我们的hive版本升迁经历了0.7.1 -> 0.8.1 -> 0.9.0,并且线上shark所依赖的hive版本也停留在0.9.0上,在这些版本上有我们自己的bug fix patch和 ...

  4. MySQL5.6中新增特性、不推荐使用的功能以及废弃的功能

    虽然已经使用MySQL5.6版本有一段时间了,但由于没有和之前的版本作过详细比较,所以对于哪些重要的或者不太重要的特性是在新版本中引入的,还有哪些特性已经或者将要从旧版本中移除的并没有一个十分全面的了 ...

  5. C#8.0中新特性之一:结构readonly成员

    结构struct成员支持readonly,用来限制被其修饰的成员不会改变结构的内部状态.加上7.2版本添加的readonly struct和ref readonly方法返回以及之前的字段声明修饰作用, ...

  6. asp.net 2.0中新增的web.config的默认namespace功能 (转)

    看上去这个题目比较长,但实际上,我在看资料时发现,这就是说,在asp.net 2.0中,只需要在web.config里定义你要用的那些namespace,则在aspx页面中就不需要再象1.1那样,用 ...

  7. ASP.Net4.0中新增23项功能

    这篇文章介绍Visual Studio 2010 (ASP.Net 4.0)的新功能. 1.代码片段(Code Snippets): 代码段是预先开发的代码模板,可以节省我们对有关语法思考的时间.在V ...

  8. .net 4.0 中的特性总结(四):Tuple类型

    Tuple是具有指定数量和顺序的值的一种数据结构.针对这种数据结构,.Net4.0中提供了一组Tuple类型,具体如下: Tuple   Tuple<T>   Tuple<T1, T ...

  9. .net 4.0 中的特性总结(一):dynamic

    在新版本的C#中,dynamic关键词是一个很重要的新特性,现在你可以创建动态对象并在运行时再决定它的类型.而且.net 4.0为CLR加入了一组为动态语言服务的运行时环境,称为DLR(Dynamic ...

随机推荐

  1. 使用vsftp搭建ftp服务

    第一步:安装vsftp pam db4 yum install vsftpd pam* db4*-y ================================================= ...

  2. PostgreSQL聚合函数的filter子句

    一张表存储了学生id,科目,分数三个字段,求每个学生60分以下与参加的总科目占比.(今天电脑不好用,图片总是这样) 其实一个count(*) filter  就可以查出来,但是没用过PG的一个人竟然说 ...

  3. 第十七章-异步IO

    异步IO的出现源自于CPU速度与IO速度完全不匹配 一般的可以采用多线程或者多进程的方式来解决IO等待的问题 同样异步IO也可以解决同步IO所带来的问题 常见的异步IO的实现方式是使用一个消息循环, ...

  4. 动作基类 CCAction

    CCAction动作基类关系 class CC_DLL CCAction : public CCObject { public: CCAction(void); virtual ~CCAction(v ...

  5. 流媒体直播服务器:Bull-Live-Server

    Bull Live Server 简称 BLS ,旨在使用C++语言提供强大功能和高性能的流媒体直播服务器. 为何要写 BLS ? 1.simple rtmp server https://githu ...

  6. 通过nginx搭建hls流媒体服务器

    通过录像文件模拟直播源,通过rtmp协议推送到nginx服务器 nginx 配置文件 增加 rtmp { server { listen 1935; application hls { live on ...

  7. bzoj 4260: REBXOR Trie+乱搞

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=4260 题解: 啊啊啊. 被这种SB题坑了半天. 求出异或前缀和后 从n到1枚举\(r_1 ...

  8. tarjian求lca

    看了好多dalao的博客,就总结一下啦ovo tarjian算法很是神奇,它的作用是求lca.它是一种离线算法. 在线是指输入一个询问输出一个结果. 离线是将询问一次性输入,一起处理. tarjan它 ...

  9. jQuery DataTables 使用手册(精简版)

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/5182940.html 前排提醒,这个插件能不用就不用,那么多好的插件等着你,为什么要用它呢?就算用easyui的 ...

  10. understanding of Pipe line & Timing Logic

    ///////////////////////////////////////////////////////////////////////////////// module vlg_add(inp ...