对于回归的 .NET 开发人员来说,C# 的新功能非常棒
在Twitter上关注我,很高兴接受您对主题或改进的建议/Chris
所以我暂时脱离了这个圈子一段时间了。实际上,我一直在写全栈 JavaScript 代码,回到 .NET 之后,我注意到 C# 语言新增了一些很棒的特性,这些特性我以前在 JavaScript 和函数式语言中都见过。这让我非常高兴,因为这意味着 C# 能够从不同的范式中汲取经验,最重要的是不断发展,变得越来越好。此外,看到 .NET Core 变得更快、更好也是一件好事 ;) .NET 可以在所有平台上运行,谁能在 2000 年初就想到这一点呢?:)
那么这些特征是什么?
参考
- 解构、元组和对象本章介绍如何对元组和对象进行解构。它将涵盖你需要了解的所有内容。
- 模式匹配 .NET 8.0 .NET 8.0 对 C# 7.0 中添加的模式匹配进行了一些更新
- 一般的模式匹配这涵盖了现有的不同类型的模式匹配。
- 记录——未来尚未到来,但可以一窥
-1-解构
这是我非常喜欢的 JavaScript 特性之一,当然在 JavaScript 中它被称为解构。那么它的具体含义是什么呢?它指的是,当你想引用一个对象的几个属性或一个列表中的项目,但又不想引用它们的全部时。我们来看一个 JS 示例:
// JavaScript example
function getPerson() {
return {
name: 'chris',
company: 'Microsoft',
title: 'Cloud Advocate'
};
}
// without destructuring
const person = getPerson();
console.log(person.name);
console.log(person.company);
// with destructuring
const { name, company } = getPerson();
console.log(name);
console.log(company);
正如你在上面看到的,我person
每次想和会员聊天就得打字。好多多余的字符 :/
那么这在 C# 中看起来怎么样?
(var pname, var pcompany) = CreatePerson("chris", "microsoft")
好的,看起来很相似。它到底
CreatePerson()
给我带来了什么?
在这种情况下,它返回一个对象,但也可以是一个元组。
好的,那么这有效吗?
嗯,这取决于你正在处理什么结构。它目前适用于元组和类,不过对于类,你需要做一些额外的工作才能启动和运行。
元组
我们先来了解一下 Tuple,以便理解它的来龙去脉。首先创建一个生成 Tuple 的方法。如下所示:
public static (string, string) GetPerson(string name, string company)
{
return (name, company);
}
我们可以用两种方法来解决这个问题:
- 请求元组中所有的变量
- 要求尽可能多的,但使用一次性字符
_
我们来演示一下第一种情况:
所有变量
var (name, company) = GetPerson("Chris", "Microsoft");
// name = "Chris"
// company = "Microsoft"
丢弃案例
var (name, _) = GetPerson("Chris", "Microsoft");
// name = "Chris"
上面我们用来_
表示我们不关心元组中的第二个参数。
如果我们有 3 个或更多参数,它们是否都存储在 中
_
?
嗯,不是。你需要为每个位置声明一个我们不关心的位置,这意味着我们需要像这样输入:
var (name, _, _) = GetPerson("Chris", "Microsoft");
课程
我们也可以对类执行 Destruct 操作,并像对 Tuple 一样访问特定字段。不过,为了实现这一点,我们需要Deconstruct()
在类中添加一个方法,如下所示:
public class Person
{
public string Name { get; set; }
public string Company { get; set; }
public string Country { get; set; }
public void Deconstruct(out string name, out string company)
{
name = Name;
company = Company;
}
public void Deconstruct(out string name, out string company, out string country)
{
name = Name;
company = Company;
country = Country;
}
}
// and access like so
(var name, var company) = new Person(){ Name = "Chris", Company ="Microsoft" };
对于我们不感兴趣的职位,同样的规则也适用,即使用_
类似如下的内容:
(var name, var company, _) = new Person(){ Name = "Chris", Company ="Microsoft" };
上面我们说的是,我们正在丢弃country
。
重载解构
你可能注意到了,我们的类中Person
有两个Deconstruct()
方法。这是因为我们进行了重载,这使得我们能够像下面这样写:
(var name, var company, _) = new Person(){ Name = "Chris", Company ="Microsoft" };
像这样:
(var name, var company) = new Person(){ Name = "Chris", Company ="Microsoft" };
上面我们实际上删除了我们的丢弃_
,因为我们现在使用Deconstruct()
带有两个参数的。
但是等一下,那么我什么时候需要丢弃
_
呢?
在以下情况下,您需要一个参数:
(var name, _ ) = new Person(){ Name = "Chris", Company ="Microsoft" };
上面我们以两个参数为目标,Deconstruct()
同时丢弃最后一个参数。
那么为什么我不能直接输入
( var name )
它就是这样实现的,我能告诉你什么呢?记住,如果使用重载,请使用两个或更多参数来使用它。
要了解有关 Deconstruct 的更多信息,请阅读此处:
-2- 模式匹配
模式匹配是我在 F# 等函数式语言中见过的。那么它的目的是什么呢?嗯,如果你问我,那就是为了减少输入,并更精确地比较你真正想要的内容。那么我们究竟在讨论什么呢?Switch-case 以及如何让它们更易于阅读和更有目的性。
请给我看看
好的。我以前在 C# 中支持对字符串、整数和枚举进行 switch case 操作,就是这样。现在别想在对象上使用它了,那样我就得用 if、else-if 语句,然后强制类型转换对象等等。我们来展示一些代码,这样大家就能理解了:
public abstract class Character
{
private string _name;
public string Name
{
get {
return _name;
}
}
public Character(string name)
{
_name = name;
}
}
public class Hero: Character
{
public Hero(string name): base("The Good" + name){}
public string Healing()
{
return "Performs healing";
}
}
public class Villain: Character
{
public Villain(string name) : base("The Evil "+ name) { }
public string Lightning()
{
return "Performs Force lightning";
}
}
现在想象一下我们有一些这样的代码:
var hero = new Hero("Luke");
var villain = new Villain("Darth Vader");
Character character = hero;
var ability = string.Empty;
现在让我们从我记得的 C# 中添加一些切换逻辑:
if(character is Hero)
{
var h = (Hero) character;
ability = h.Healing();
} else {
var v = (Villain) character;
ability = v.Lightning();
}
我们对此感到高兴吗?不,真的不高兴。不过好消息是,根据 C# 7,我们可以这样做:
switch(character)
{
case Hero hero1:
ability = hero1.Healing();
break;
case Villain villain1:
ability = villain1.Lightning();
break;
}
上面我们创建了对象hero1
,或者villain1
根据具体情况进行创建。这样可以减少一些输入。
拿着我的啤酒,让我们开始 C# 8.0 吧 :)
ability = character switch {
Hero hero2 => hero2.Healing(),
Villain villain2 => villain2.Lightning(),
_ => "Unknown"
};
看起来确实漂亮又紧凑:)
正确的?
位置模式
C# 8 简直让我们惊叹不已,至少我是这样的 :) 那么,我们还有什么呢?我们称之为位置模式。这种模式使我们能够检查对象,并根据其设置的属性采取相应的措施。
假设我们有一张发票。根据它的状态,我们可能想以不同的方式处理它。那么,在给定模式匹配的 switch-case 语句中,我们该如何表达呢?
好吧,在我们开始之前,我们先假设我们将使用Destruction来实现这一点。接下来的问题是,哪些值可能值得关注。假设类 , 具有以下属性Invoice
。BossSigned
我们SkipLevelBossSigned
制定一条业务规则,规定如果老板和上级老板都签署了发票,则可以处理该发票。
BossSigned
,具有价值null
或签署日期SkipLevelBossSigned
,具有价值null
或签署日期
var result = invoice switch
{
Invoice (null, null) => "Boss need to sign",
Invoice (_ , null) => "Skip level boss need to sign",
_ => "All good, process"
}
属性模式
到目前为止,我们匹配了类型Invoice
,但我们也可以添加一些我们不关心类型,只关心形状的东西
var result = shape switch
{
{ BossSigned: null, SkipLevelBossSigned: null } => "Boss need to sign",
{ SkipLevelBossSigned: null } => "Skip level boss need to sign",
_ => "All good, process"
}
不过,上面的代码存在风险,如果_
值为 null,则会触发 default case。在投入生产之前,你可能需要修复这段代码。
-3- 记录
这东西还没落地,但我真是兴奋极了。它是什么?嗯,简而言之,忘掉字段声明吧。
你是认真的吗?我会得到什么?看起来会怎么样?
好的,对我来说,这得益于 Scala 和 Typescript,它们能够帮你完成繁重的工作,也就是创建字段。在 TypeScript 中如下所示:
class Hero {
constructor(private name: String, private hp: number, public shout: String)
}
上面的想法是为你创建字段name
、hp
和shout
。这样你就不必像这样输入它们了:
// the above would compile to this
class Hero {
constructor(name: String, hp: number, shout: String) {
this.name = name;
this.hp = hp;
this.shout = shout;
}
}
作为一名 C# 开发人员,这看起来有点落后,类型最后?
它让我想起了这个模因,但是,它就是这样的:)
如果用 C# 实现会怎么样?他们正在努力,但提案应该是这样的:
public class Hero(String name, int hp, String shout);
编译后会变成这样:
public class Hero : IEquatable<Hero>
{
public String name;
public int hp;
public String shout;
public Hero(string name, int hp, string shout)
{
this.name = name;
this.hp = hp;
this.shout = shout;
}
public bool Equals(Hero other)
{
//
}
public override bool Equals(object other)
{
//
}
public override int GetHashCode()
{
//
}
public void Deconstruct(out string name, out int hp, out string shout)
{
name = name;
hp = hp;
shout = shout;
}
public Hero With(string name = this.name, int age = this.age, string shout = this.shout) => new Hero(name, hp, shout);
}
因此,我们会获得很多额外的功能,例如 Deconstruct、Equal 以及使用新语法With
。With
这也是一个新功能,可以帮助我们创建新的不可变对象。它允许我们像这样输入:
var hero = GetHero(); // get a Hero object from somewhere
var newHero = hero.With(hp = 23)
// alt syntax
var otherHero = hero with { hp = 23 };
概括
本文到此结束。如果你和我一样回归.NET,你会发现它和.NET Core 一样,但又有所不同。哦对了,.NET Core 绝对是个重磅消息。它能够在 Linux/Mac 上运行 .NET,容器化你的代码等等。谁能想到这一天会到来呢?
那么坦白说,我会使用这些结构吗?我肯定会使用模式匹配,即使我很多时候会求助于继承,从而避免使用这种逻辑。
那么解构呢?我的意思是,对于元组来说,确实如此,它不需要额外的工作就能正常工作。对于对象呢?我觉得自己会做以下两件事之一:
- 在类上创建一个方法,返回我需要的元组
- 是的,
Deconstruct()
在合适的地方添加方法。不过我会问自己:我应该把字段从类传递到外部上下文吗?字段不是实现细节吗?但是属性呢?我们有包含很多公共字段的数据传输对象,所以
记录,我的生活需要它们。迫不及待地想看到它们最终实现 :)
文章来源:https://dev.to/dotnet/great-new-features-in-c-for-a-returning-net-dev-i2l