?目錄
什么是record?
record是.NET 5中的一種新特性,可以看作是一種概念上不可變的類。records可以幫助我們在C#中更容易地處理數(shù)據(jù),同時提供了重要的功能,如對象相等性、hashcode和解構(gòu)。
與類不同,records具有值語義。也就是說,當(dāng)比較兩個records的實(shí)例時,比較的是這些實(shí)例的屬性而非引用。這意味著,如果兩個records的屬性值相同,它們就是相等的。
record也可以簡化需要類似于Dto的數(shù)據(jù)結(jié)構(gòu)容器的定義。
使用record
Person p1 =new("小明","南山","11@outlook.com");
Person p2 =new("小明","南山","11@outlook.com");
Console.WriteLine(p1 == p2);publicrecord Person(stringName,stringAddress,stringEmail);
像定義一個類一樣,public class Person,只是將class關(guān)鍵字替換成record關(guān)鍵字。然后屬性是用括號來定義。
默認(rèn)的record聲明是class,如果想聲明一個struct
publicrecordstructPerson(stringName,stringAddress,stringEmail);
record是不可變的類型,括號中聲明的屬性在構(gòu)造之后不可變更??梢允褂?=按屬性的值進(jìn)行比較??梢灾苯幼鳛閔ash的key以及結(jié)構(gòu)。
record可以像普通類一樣擴(kuò)展可變更的屬性和自定義的方法,語法如下
publicrecord Person(stringName,stringAddress,string Email)
{
? ? publicrequiredstringPhoneNumber {get;set; }
? ? publicstaticIEnumerable GetAll()
? ? {
? ? ? ? yieldreturnnewPerson("張三","123 Main St","john@example.com") { PhoneNumber ="123456789"};
? ? ? ? yieldreturnnewPerson("李四","456 Elm St","jane@example.com") { PhoneNumber ="123456789" };
? ? ? ? yieldreturnnewPerson("王二","789 Oak St","bob@example.com") { PhoneNumber ="123456789" }; ;
? ? }
? ? publicstringGetDisplayName() => $"{Name} ({Email})";
};
record解構(gòu)
record可以通過解構(gòu),將對象解構(gòu)為元組,方便一次性獲取record中的屬性值,
Person p1 =new("小明","南山","11@outlook.com") ;var(name,address,email) = p1 ;
record原理
record的原理是編譯器提供支持,上述Person定義反編譯結(jié)果如下
publicclassPerson : IEquatable{
? ? [CompilerGenerated]
? ? protectedvirtual Type EqualityContract
? ? {
? ? ? ? [CompilerGenerated]
? ? ? ? get? ? ? ? {
? ? ? ? ? ? returntypeof(Person);
? ? ? ? }
? ? }
? ? publicstringName {get;set/*init*/; }
? ? publicstringAddress {get;set/*init*/; }
? ? publicstringEmail {get;set/*init*/; }
? ? publicPerson(stringName,stringAddress,string Email)
? ? {
? ? ? ? this.Name = Name;
? ? ? ? this.Address = Address;
? ? ? ? this.Email = Email;
? ? ? ? base..ctor();
? ? }
? ? [CompilerGenerated]
? ? publicoverridestring ToString()
? ? {
? ? ? ? StringBuilder stringBuilder =new StringBuilder();
? ? ? ? stringBuilder.Append("Person");
? ? ? ? stringBuilder.Append(" { ");
? ? ? ? if (PrintMembers(stringBuilder))
? ? ? ? {
? ? ? ? ? ? stringBuilder.Append('');
? ? ? ? }
? ? ? ? stringBuilder.Append('}');
? ? ? ? return stringBuilder.ToString();
? ? }
? ? [CompilerGenerated]
? ? protectedvirtualbool PrintMembers(StringBuilder builder)
? ? {
? ? ? ? RuntimeHelpers.EnsureSufficientExecutionStack();
? ? ? ? builder.Append("Name = ");
? ? ? ? builder.Append((object?)Name);
? ? ? ? builder.Append(", Address = ");
? ? ? ? builder.Append((object?)Address);
? ? ? ? builder.Append(", Email = ");
? ? ? ? builder.Append((object?)Email);
? ? ? ? returntrue;
? ? }
? ? [CompilerGenerated]
? ? publicstaticbooloperator!=(Person? left, Person? right)
? ? {
? ? ? ? return!(left == right);
? ? }
? ? [CompilerGenerated]
? ? publicstaticbooloperator==(Person? left, Person? right)
? ? {
? ? ? ? return(object)left == right || (left?.Equals(right) ??false);
? ? }
? ? [CompilerGenerated]
? ? publicoverrideint GetHashCode()
? ? {
? ? ? ? return((EqualityComparer.Default.GetHashCode(EqualityContract) * -1521134295+ EqualityComparer.Default.GetHashCode(Name)) * -1521134295+ EqualityComparer.Default.GetHashCode(Address)) * -1521134295+ EqualityComparer.Default.GetHashCode(Email);
? ? }
? ? [CompilerGenerated]
? ? publicoverrideboolEquals(object? obj)
? ? {
? ? ? ? returnEquals(objas Person);
? ? }
? ? [CompilerGenerated]
? ? publicvirtualboolEquals(Person? other)
? ? {
? ? ? ? return(object)this== other || ((object)other !=null&& EqualityContract == other.EqualityContract && EqualityComparer.Default.Equals(Name, other.Name) && EqualityComparer.Default.Equals(Address, other.Address) && EqualityComparer.Default.Equals(Email, other.Email));
? ? }
? ? [CompilerGenerated]
? ? protected Person(Person original)
? ? {
? ? ? ? Name = original.Name;
? ? ? ? Address = original.Address;
? ? ? ? Email = original.Email;
? ? }
? ? [CompilerGenerated]
? ? publicvoidDeconstruct(outstringName,outstringAddress,outstring Email)
? ? {
? ? ? ? Name =this.Name;
? ? ? ? Address =this.Address;
? ? ? ? Email =this.Email;
? ? }
}
?可以看到,編譯器給使用了record關(guān)鍵字的定義生成了對應(yīng)的屬性和構(gòu)造函數(shù),并且重寫了ToString(),GetHashCode,Equals還有一個解構(gòu)函數(shù)和!=和==運(yùn)算符。其實(shí)看到這里就明白了,為什么record可以提供值比較,解構(gòu),hash等。
不可變性是因?yàn)閞ecord的屬性是使用了init關(guān)鍵字而不是set,這樣子如果對record的對象屬性賦值,編譯器會報錯。
值相等性是重定義了!=和==運(yùn)算符
hash是因?yàn)橹貙懥薌etHashCode,Equals
解構(gòu)是定義了Deconstruct方法
結(jié)論
我們介紹了.NET 5中引入的record類型及其優(yōu)點(diǎn)。但對于許多數(shù)據(jù)對象的簡單情況,如值對象和DTO,推薦使用record類型。雖然record可以定義可變更的屬性和添加方法,不過這樣子有點(diǎn)違背了record的初衷。