下载APP

C# 便捷语法一

本文包含以下便捷语法:

  • 属性在声明处即可进行初始化
  • Expression-bodied 函数成员
  • using static增强功能
  • Null 条件运算符?. ??
  • 字符串的格式化
  • 异常筛选器
  • nameof 表达式
  • Catch 和 Finally 块中的 Await

属性在声明处即可进行初始化

在声明它的位置处被初始化。 这样,就能更容易地仅执行一次初始化。 如下:

public string FeatureOne { get; } = "NewFeature_One";
public ICollection<double> Grades { get; } = new List<double>();
public string AutoInit { get; set; } = "Auto Init";

Expression-bodied 函数成员

当我们编写的函数主体只包含一条可以表示为表达式的语句时,可通过改为编写 expression-bodied 成员来简化该语法。 比如:

public override string ToString() => $"{LastName}, {FirstName}";

还可以在只读属性中使用 expression-bodied 成员:(以下语句将会被编译成只读属性)

public string FullName => $"{FirstName} {LastName}";
// Expression-bodied get / set accessors.
public string Label {
    get => label;
    set => this.label = value ?? "Default label";
}

using static增强功能

using static 增强功能可用于导入单个类的静态方法。 以前,using 语句将所有类型导入命名空间中。 比如,我们只需要使用System命名空间下的Math类,其他的类我们并不使用,此时我们可以通过以下方式引入Math类

using static System.Math;

通过这种方式引入后,使用方式也与之前有一定的变化,如下:

// 之前的使用方式
Math.Abs(1 + 2.5);
// 使用 using static System.Math;之后的方式
Abs(1 + 2.5);

切记,这种引入方式只会引入该声明类中的静态方法。引入后的静态方法可当成全局函数使用,但其作用范围需要分为文件顶部引入和命名空间内部引入(参见温故之C# using注意事项

Null 条件运算符?. ??

?.运算符

在代码中,为了保证每个引用不会抛出NullException异常,我们往往需要进行如下判断:

Feature feature = GetFeature();
if(tmpValue != null) {
  Console.WriteLine(feature.ToString());
}

而有了 Null 条件运算符之后,我们的写法可简化为如下feature?.ToString()

Feature feature = GetFeature();
Console.WriteLine(feature?.ToString());

相信,此运算符在事件判断上更是会广泛使用,比如有Saved事件,现在,我们可以以更加简洁且更加线程安全的方式访问了,如下

Saved?.Invoke(this, eventArgs);
??运算符

此运算符可保证在没有合法值之时,为变量提供一个默认值。 在之前,是这样的:

string obj = null;
string res = string.Empty;
if (obj == null) {
  obj = "this is default value";
}else {
  res = obj;
}
Console.WriteLine(res);

而现在,是这样的:

string obj = null;
string res = obj ?? "this is default value";
Console.WriteLine(res);

看,现在是不是简洁了很多呢?另外,如果需要抛出异常,则可以按以下方式:

string obj = null;
string res = obj ?? throw new ArgumentNullException(paramName: nameof(obj), message: "must not be null");
Console.WriteLine(res);

字符串的格式化

在之前,我们格式化字符串方式如下:

string tmp = string.Format("{0} {1}", FirstName, LastName);

现在,我们可以以更加方便的方式处理:

string tmp = $"{FirstName} {LastName}";

不知道您是否发现了它的优势?除了比之前更加简洁之外,更重要的是,当需要格式化的参数很多(比如5个以上时),很容易将这些参数的顺序混淆,而现在,您所写的,就是它存在的顺序,减少了出错的概率(甚至根本不会出错)。

异常筛选器

异常筛选器是确定何时应该应用给定的 catch 子句的子句。如果用于异常筛选器的表达式计算结果为 true,则 catch 子句将对异常执行正常处理。 如果表达式计算结果为 false,则将跳过 catch 子句。使用方式如下:

public static async Task<string> MakeRequest() { 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    }
}

nameof 表达式

nameof 表达式的计算结果为符号的名称。 每当需要变量、属性或成员字段的名称时,这是让工具正常运行的好办法。 nameof 的其中一个最常见的用途是提供引起异常的符号的名称,如下:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

这样写有个好处就是,假如我们需要使用重构工具重命名符号, 更改lastNamefirstName,则会在 nameof 表达式中对其重命名。而字符串硬编码的方式则没有这一优势,很容易被忽略。

如果您写过WPF程序,那么您肯定使用过INotifyPropertyChanged, 之前要实现属性更改的通知功能,实现方式如下:

public string LastName {
    get { return lastName; }
    set {
        if (value != lastName) {
            lastName = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LastName"));
        }
    }
}
private string lastName;

而现在,PropertyChangedEventArgs的参数,可使用nameof(LastName):

public string LastName {
    get { return lastName; }
    set {
        if (value != lastName) {
            lastName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(LastName)));
        }
    }
}
private string lastName;

Catch 和 Finally 块中的 Await

catch 表达式中使用 await,这通常用于日志记录方案。如下:

public static async Task<string> MakeRequestAndLogFailures() { 
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301")) {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    } finally {
        await logMethodExit();
        client.Dispose();
    }
}

不过,个人尚未发现此记录方式的优势,暂不推荐使用。一般地,在项目中需要记录日志,会采用单独的服务来处理日志,即有异常时,立即将异常信息发送给日志记录服务,而程序本身继续下一步。使用的较多的是采用Kafka来进行日志的记录,关于Kafka,您可以百度,如果还有疑惑,咨询我也可以。

至此,本节内容讲解完毕

给个关注呗,让我们一起学习互联网技术