一篇文章带你掌握 C# 5/6/7 新特性

本文介绍了C#从3.5到7.0版本的重要新特性,包括扩展方法、异步编程、自动属性初始化等。重点讲解了C#5.0的异步I/O、C#6.0的自动属性初始化及主构造器、C#7.0的输出变量、模式匹配和元组等内容。

语法升级实战示例 ,一次梳理三大版本高频特性


目录

  1. 前言:为什么要学习新语法?

  2. C# 3.5 回顾:扩展方法的正确打开方式

  3. C# 5.0 新特性
    3.1 基于 TAP 的异步编程
    3.2 在 Lambda 中使用循环变量

  4. C# 6.0 新特性
    4.1 自动属性初始化器
    4.2 主构造器
    4.3 字典初始化器
    4.4 声明表达式
    4.5 静态 using
    4.6 await in catch/finally
    4.7 异常过滤器
    4.8 空值条件运算符 ?. 与 ??

  5. C# 7.0 新特性
    5.1 输出变量
    5.2 模式匹配
    5.3 元组与解构
    5.4 局部函数
    5.5 数字改进与二进制字面量
    5.6 ref return 与 ref local
    5.7 更多 Expression-bodied 成员
    5.8 Throw 表达式

  6. 小结与最佳实践

  7. 互动:你的点赞就是对作者最大的支持!


前言:为什么要学习新语法?

技术只有适用与否,没有新旧之分
——但懂得更多,你就拥有更多选择。

工作中我们经常遇到两类代码:

  1. 历史包袱:C# 3.0 甚至更早的写法,能跑但难维护。

  2. 现代语法:简洁、类型安全、具备更好的可读性与性能。

掌握 C# 5/6/7 不仅能写出更优雅的代码,还可在重构旧系统时游刃有余。本篇文章通过示例 + 场景分析,帮助你快速吸收每个版本的亮点特性。


C# 3.5 回顾:扩展方法的正确打开方式

虽然本文聚焦 5/6/7,但先用扩展方法热热身,因为它是后续很多语法糖的灵魂伴侣。

代码示例

public static class ConvertExtensions
{
    // 将 object 安全地转成 string
    public static string AsString(this object value) => value?.ToString() ?? string.Empty;

    // 将 string 转成 int,失败则返回默认值
    public static int AsInt32(this string value) =>
        int.TryParse(value, out var i) ? i : 0;
}

使用方式:

int number = "123".AsInt32();      // 123
int zero   = "abc".AsInt32();      // 0

优点

  1. 链式调用,逻辑自然。

  2. 摆脱 try...catch 嵌套,提高可读性。


C# 5.0 新特性

基于 TAP 的异步编程

async/await 让你像写同步代码一样编写异步逻辑,极大减少回调地狱。

// ✅ 推荐写法
static async Task DownloadAsync(Uri uri)
{
    using var client = new HttpClient();
    string html = await client.GetStringAsync(uri);
    Console.WriteLine(html);
}

对比旧写法:

// ❌ 回调层层嵌套,可读性差
static void DownloadOld(Uri uri)
{
    var client = new WebClient();
    client.DownloadStringCompleted += (s, e) => Console.WriteLine(e.Result);
    client.DownloadStringAsync(uri);
}

最佳实践
• 使用 ConfigureAwait(false) 在库代码中避免死锁。
• 优先返回 Task / Task<T> 而非 void

在 Lambda 中使用循环变量

C# 5 修复了循环捕获变量的“坑”。

var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
    int local = i;                 // 显式拷贝
    actions.Add(() => Console.WriteLine(local));
}

执行后输出 0 1 2,不再全部是 3


C# 6.0 新特性

自动属性初始化器

public class BlogPost
{
    public long   Id   { get; } = 1;
    public string Name { get; } = "Hello C# 6.0";
    public string Title { get; private set; } = string.Empty;
}

• 少写一个构造函数,属性仍可在需要时 set

主构造器

public class BlogPost(long id, string name, string title)
{
    public long   Id    { get; } = id;
    public string Name  { get; } = name;
    public string Title { get; } = title;
}

参数与字段绑定一目了然。

字典初始化器

var dict = new Dictionary<string, string>
{
    ["Home"]     = "https://blog.csdn.net",
    ["Features"] = "C# 6 Highlights"
};

声明表达式

if (!int.TryParse(input, out var number))
{
    Console.WriteLine("非法数字");
}

out var 直接声明,少了一次定义。

静态 using

using static System.Console;

WriteLine("无需前缀即可调用 WriteLine");

await in catch/finally

try
{
    await DoWorkAsync();
}
catch (Exception ex)
{
    await LogAsync(ex);
}

异常过滤器

catch (CustomException ex) when (ex.Level > 5)
{
    // 仅处理严重错误
}

空值条件运算符 ?. 与 ??

var rank = user?.Rank ?? "No Rank";

一行代码避免多重 if (user != null) 判空。


C# 7.0 新特性

输出变量

if (int.TryParse(text, out var value))
{
    Console.WriteLine($"合法数字:{value}");
}

无需提前声明 int value;

模式匹配

switch (shape)
{
    case Circle c:
        Console.WriteLine($"Circle r={c.Radius}");
        break;

    case Rectangle s when s.Length == s.Height:
        Console.WriteLine("正方形");
        break;

    case Rectangle r:
        Console.WriteLine($"矩形 {r.Length}x{r.Height}");
        break;

    case null:
        throw new ArgumentNullException(nameof(shape));

    default:
        Console.WriteLine("未知形状");
        break;
}

元组与解构

// 返回多个值
(string first, string last) GetName() => ("Tom", "Jerry");

// 解构
var (f, l) = GetName();
Console.WriteLine($"{f} {l}");

局部函数

int Fibonacci(int n)
{
    if (n < 0) throw new ArgumentException();

    return Fib(n).current;

    (int current, int previous) Fib(int i)
        => i == 0 ? (1, 0) : (
            Fib(i - 1).current + Fib(i - 1).previous,
            Fib(i - 1).current);
}

数字改进与二进制字面量

int million = 1_000_000;
int flags   = 0b1010_1011;

ref return 与 ref local

public ref int Find(int target, int[] nums)
{
    for (var i = 0; i < nums.Length; i++)
        if (nums[i] == target) return ref nums[i];

    throw new IndexOutOfRangeException();
}

ref int place = ref Find(7, array);
place = 9;                // 修改原数组

更多 Expression-bodied 成员

class Person(string name)
{
    public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
    ~Person() => Console.WriteLine("Finalized");
}

Throw 表达式

string GetFirst(string[] arr) => 
    (arr?.Length > 0) ? arr[0] : throw new InvalidOperationException("空数组");

小结与最佳实践

  1. 从可读性出发:善用模式匹配、元组、解构让意图更清晰。

  2. 从性能出发:异步 I/O + ConfigureAwait(false) + ValueTask<T>(C# 7.0 之后)减少上下文切换与堆分配。

  3. 从可维护性出发:局部函数、静态 using、空值运算符让代码更 DRY(Don’t Repeat Yourself)。

在重构旧项目时,可以渐进式引入新语法:
• 先用 nameof???. 安全替换魔法字符串与判空;
• 再将老式 Begin/End 异步重写为 async/await
• 最后尝试使用模式匹配、元组重构 Core 逻辑。


互动:你的点赞就是对作者最大的支持!

如果本文对你有所启发:

  1. 点个 👍 让更多人看到;

  2. 关注专栏,第一时间获取 .NET 最新干货;

  3. 评论区聊聊你最喜欢的 C# 新特性 ,一起切磋提升!

知识的世界无限大,让我们一起保持学习的热情!

更多AIGC文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许泽宇的技术分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值