C# 5.0中新增特性
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中新增特性的更多相关文章
- Performance Tuning guide 翻译 || Performance Tuning Guide 11G中新增特性
CSDN 对格式支持比較弱.能够到http://user.qzone.qq.com/88285879/blog/1399382878 看一致的内容. Performance Tuning Guide ...
- 第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 ...
- hive 0.10 0.11新增特性综述
我们的hive版本升迁经历了0.7.1 -> 0.8.1 -> 0.9.0,并且线上shark所依赖的hive版本也停留在0.9.0上,在这些版本上有我们自己的bug fix patch和 ...
- MySQL5.6中新增特性、不推荐使用的功能以及废弃的功能
虽然已经使用MySQL5.6版本有一段时间了,但由于没有和之前的版本作过详细比较,所以对于哪些重要的或者不太重要的特性是在新版本中引入的,还有哪些特性已经或者将要从旧版本中移除的并没有一个十分全面的了 ...
- C#8.0中新特性之一:结构readonly成员
结构struct成员支持readonly,用来限制被其修饰的成员不会改变结构的内部状态.加上7.2版本添加的readonly struct和ref readonly方法返回以及之前的字段声明修饰作用, ...
- asp.net 2.0中新增的web.config的默认namespace功能 (转)
看上去这个题目比较长,但实际上,我在看资料时发现,这就是说,在asp.net 2.0中,只需要在web.config里定义你要用的那些namespace,则在aspx页面中就不需要再象1.1那样,用 ...
- ASP.Net4.0中新增23项功能
这篇文章介绍Visual Studio 2010 (ASP.Net 4.0)的新功能. 1.代码片段(Code Snippets): 代码段是预先开发的代码模板,可以节省我们对有关语法思考的时间.在V ...
- .net 4.0 中的特性总结(四):Tuple类型
Tuple是具有指定数量和顺序的值的一种数据结构.针对这种数据结构,.Net4.0中提供了一组Tuple类型,具体如下: Tuple Tuple<T> Tuple<T1, T ...
- .net 4.0 中的特性总结(一):dynamic
在新版本的C#中,dynamic关键词是一个很重要的新特性,现在你可以创建动态对象并在运行时再决定它的类型.而且.net 4.0为CLR加入了一组为动态语言服务的运行时环境,称为DLR(Dynamic ...
随机推荐
- 每个程序员都应该了解的 CPU 高速缓存
每个程序员都应该了解的 CPU 高速缓存 英文原文:Memory part 2: CPU caches 来源:oschina [编者按:这是Ulrich Drepper写“程序员都该知道存储器”的第二 ...
- sqlite3简单教程整理
一.Ubuntu下安装sqlite3 1.介绍:sqlite3是linux上的小巧的数据库,一个文件就是一个数据库. 2.安装: 要安装sqlite3,可以在终端提示符后运行下列命令: sud ...
- 学习HTML5
CSS,层叠样式表,能为网页增添样式的电脑语言. UL属于无序列表 OL属于有序列表 DL属于自定义列表.
- beans.factory.BeanCreationException beans.factory.annotation.Autowired(required=true)
主要是这三个方面排查: 1,注入写成这样 @Autowired private BrandServiceImpl brandServiceImpl; 2,jar冲突,在pom.xml中 ...
- python-Django收集主机信息
1.创建工程simplecmdb django-admin.py startproject simplecmdb 2.创建应用 cd simplecmdb python manage.py start ...
- android 网络编程--socket tcp/ip udp http之间的关系
网络七层由下往上分别为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层,一般编程人员接触最多的就是应用层和运输层,再往下的就是所谓的媒体层了,不是我们研究的对象. 下面是应用层.运输层,网络 ...
- 雅礼集训 2017 Day2 水箱 可并堆
题目描述 给出一个长度为 n 宽度为 1 ,高度无限的水箱,有 n−1 个挡板将其分为 n 个 1 - 1 的小格,然后向每个小格中注水,水如果超过挡板就会溢出到挡板的另一边,这里的水是满足物理定律 ...
- Wannafly #4 F 线路规划
数据范围252501 劲啊 Q国的监察院是一个神秘的组织. 这个组织掌握了整个Q国的地下力量,监察着Q国的每一个人. 监察院一共有N个成员,每一个成员都有且仅有1个直接上司,而他只听从其上直接司的命令 ...
- bzoj 2510: 弱题 概率期望dp+循环矩阵
题目: Description 有M个球,一开始每个球均有一个初始标号,标号范围为1-N且为整数,标号为i的球有ai个,并保证Σai = M. 每次操作等概率取出一个球(即取出每个球的概率均为1/M) ...
- logback个人使用配置
提供一个目前个人供词使用的,无需日志汇总的日志配置文件: <?xml version="1.0" encoding="UTF-8"?> <co ...