C#版本和.NET版本以及VS版本的对应关系

本文介绍了C#5.0版本中的新特性,包括异步编程模型、调用方信息、绑定运算符等,并提供了示例代码,有助于开发者理解和掌握这些新特性。

之所以在这里分享这个对应关系,是因为在C#基础知识系列的文章发布之后,有些初学者对.NET版本和C#语言特性之间的对应关系有点不清楚,有时候会弄混淆了。

并且通过这个对应关系,也可以帮助大家对C#和.NET 类库有个全面的把控,可以帮助大家理清楚C#和.NET 类库中各个知识点,使他们可以对号入坐。具体他们的之间对应关系见下表:

版本.NET Framework版本Visual Studio版本发布日期特性
C# 1.0.NET Framework 1.0Visual Studio .NET 20022002.1委托
事件
C# 1.1.NET Framework 1.1Visual Studio .NET 20032003.4APM
C# 2.0.NET Framework 2.0Visual Studio 2005(开始命名为Visual Studio)2005.11泛型
匿名方法
迭代器
可空类型
C# 3.0.NET Framework 3.0Visual Studio 20082007.11隐式类型的部变量
.NET Framework 3.5对象集合初始化
自动实现属性
匿名类型
扩展方法
查询表达式
Lambda表达式
表达式树
分部类和方法
Linq
C# 4.0.NET Framework 4.0Visual Studio 20102010.4动态绑定
命名和可选参数
泛型的协变和逆变
互操作性
C# 5.0.NET Framework 4.5Visual Studio 20122012.8异步和等待(async和await)
调用方信息(Caller Information)

这里写图片描述


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, 0, buffer.Length,
                            readResult =>
                            {
                                if (readResult.CompletedSynchronously) return;
                                readWriteLoop(readResult);
                            }, null);
                        if (!iar.CompletedSynchronously) return;
                        break;
                    case false:
                        int numRead = source.EndRead(iar);
                        if (numRead == 0)
                        {
                            return;
                        }
                        iar = destination.BeginWrite(buffer, 0, 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, 0, buffer.Length)) != 0)
        {
            await destination.WriteAsync(buffer, 0, numRead);
        }
    }

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

2. 调用方信息

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

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

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

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

public void TraceMessage(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0)
    {
        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


C# 5.0五大新特性

第一:绑定运算符,:=:

这个只是简化了数据绑定,跟ASP.NET MVC3不断改进一样,其实不是什么亮点改进。

    comboBox1.Text :=: textBox1.Text; //将文本框的内容绑定到下拉框。  

第二:带参数的泛型构造函数:

这个的加入给一些设计增加了强大功能,泛型早在C#2.0加入后就有着强大的应用,一般稍微设计比较好的框架,都会用到泛型,c#5.0加入带参数泛型构造函数,则在原有基础上对C#泛型完善了很多。:)

    public class T MyClass : T: class, new()  
    public class T MyClass : T:class, new(int)  

第三:支持null类型运算:

此功能,个人觉得并非什么大的亮点,但至少对null类型,特别是有数据计算的这种null类型的支持,写代码还是方便不少。

注意对于Nullable Types,在C#2.0就加入进来了,但是不支持计算,比如:

int? x = null;

int? y = x + 40;
那么y值是多少?不支持计算,得到的是null,想必大家知道为什么结果是null了吧?但C#5.0可以,40加一个null的整数,我们要的结果是40,不过份吧?

    int x? = null;  
    int y? = x + 40;  
    Myobject obj = null;  
    Myotherobj obj2 = obj.MyProperty ??? new Myotherobj();  

第四:case支持表达式:

这个是一个我很早就想如果能这样就好了,没想到在C#5.0里就加入此功能,以前case里只能写一个具体的常量,而现在可以加表达式了,灵活多了。

    switch(myobj){  
    llorEmpty(myotherobj):  
    //逻辑代码  
    case myotherobj.Trim().Lower:  
    //逻辑代码  
    }  

第五:扩展属性。

我们在C#3.0里有扩展方法,那么在C#5.0里将会加入扩展属性的感念,对照扩展方法,不难理解扩展属性的概念了。以下为扩展属性的定义举例:

[Associate(string)]

public static int Zivsoft_ExtensionProperty { get;set;}

C#5.0 远远不只是上面描述的5点新功能,它如同C#4.0加入dynamic概念一样,会加入异步处理概念,这个不是几行代码就能表达,而是将在设计,架构上,又会掀起一次飞跃……

为了大家抢先看,就给一段C#5.0一段简单的异步操作的代码例子,注意(C#5.0两个新加的关键字async, await):

    Task<Movie> GetMovieAsync(string title);  

    Task PlayMovieAsync(Movie movie);  

    async void GetAndPlayMoviesAsync(string[] titles)  
    {  
        foreach (var title in titles)  
         {  
             var movie = await GetMovieAsync(title);  

             await PlayMovieAsync(movie);  
         }  
    }  

以下是关于C#版本.NET版本对应关系及查看方法的详细说明: --- ### **1. 快速查看C#版本的方法** #### **方法1:项目文件(.csproj)** ```xml <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <!-- .NET 8对应C# 12 --> <LangVersion>12</LangVersion> <!-- 可手动指定C#版本 --> </PropertyGroup> ``` #### **方法2:Visual Studio** - 右键项目 → **属性** → **生成** → **高级** → 查看"语言版本" - 或通过 **解决方案资源管理器** 直接编辑`.csproj`文件 #### **方法3:命令行** ```bash # 查看已安装的.NET SDK版本 dotnet --list-sdks # 查看运行时版本 dotnet --list-runtimes ``` --- ### **2. .NET 8与C#版本对应关系** | .NET版本 | 默认C#版本 | 关键语言特性 | |----------|------------|--------------| | **.NET 8** | C# 12 | 主构造函数、集合表达式、别名任意类型 | | .NET 7 | C# 11 | 泛型数学、raw string literals | | .NET 6 | C# 10 | 全局using、文件级命名空间 | --- ### **3. 如何验证当前代码使用的C#版本** #### 代码中直接检测: ```csharp // 在代码中输出当前C#版本 Console.WriteLine($"C#版本: {typeof(string).Assembly.ImageRuntimeVersion}"); // 或通过编译器指令(需手动定义) #if CSHARP12 Console.WriteLine("运行在C# 12环境"); #endif ``` #### 编译器符号表: | 版本 | 预定义符号 | |------------|------------| | C# 12 | `CSHARP12` | | C# 11 | `CSHARP11` | --- ### **4. 重要注意事项** 1. **版本覆盖规则**: - 如果未显式指定`<LangVersion>`,SDK会自动选择**最高支持的C#版本** - 可强制指定低版本(如`<LangVersion>11</LangVersion>`) 2. **兼容性**: - .NET 8项目**可以**使用C# 11(通过`LangVersion`降级) - 但C# 12特性(如集合表达式)在低版本SDK中会编译失败 --- ### **5. 推荐实践** - **新项目**:直接使用.NET 8 + C# 12 - **旧项目迁移**:逐步升级语言版本并修复兼容性问题 - **团队协作**:在`.editorconfig`中统一配置: ```ini [*.cs] dotnet_diagnostic.CS8021.severity = error # 强制使用最新语法 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值