淺談C# record關鍵字

環境:.net8控制臺

init關鍵字

通常我們會有一個常見的需求就是需要實現一個實例化后不可變的類型.

我通常會如下實現,將類的屬性的set設為私有,這樣只能使用構造函數來實例一個不可變對象.

但是如果內部再聲明一個public的方法還是有可能會將我這個對象改變.

    internal class Program{static void Main(string[] args){Person person = new Person(1, "tom");person.SetValue(2, "Trump");Console.WriteLine(person.Name);Console.WriteLine(person.Id);}}    public class Person{public int Id { get; private set; }public string Name { get; private set; }public Person(int Id, string Name){this.Id = Id;this.Name = Name;}public void SetValue(int Id, string Name){this.Id = Id;this.Name = Name;}}

但我們可以使用init關鍵字取代原來的private set,這樣即便想在類內部設置一個方法修改屬性也是不成立的了,因為此時編譯器要求只能在聲明時賦值,構造函數中賦值和對象初始化器賦值,而禁止其他形式的賦值.?

?

什么是對象初始化器賦值?

使用這個{Id=1,Name="Tom"},這樣的形式就是對象初始化器賦值.這是一種語法糖.如下代碼

聲明時賦值,構造函數中賦值和對象初始化器賦值,這三種賦值也是有順序的

首先是聲明時賦值,然后是構造器賦值,最后是對象初始化器賦值.

雖然有了init關鍵字幫助我們實現了對象的屬性的不可變,但還不夠,一般還伴隨著要重新Tostring,Equals等方法.

通常我們還希望兩個屬性一致的對象是相等的,這我們就不得不重新Equals.幾個類倒也沒什么,但是如果這樣的類多了,我們就做了很多重復的工作,還好.net為我們提供了record關鍵字.

?record關鍵字

現在我們只需要一行就能完美實現上述需求.

但是我們有必要知道的是init關鍵字和record在實現上沒有關系.只是在設計理念上有相似的地方,同時要知道的是init比record更"寬松".

寬松如何理解?

前面我們提到init可以在對象初始化器中賦值,然后屬性才會被凍結,這其實就是在構造函數結束后還有機會再次被賦值,而record聲明的類,嚴格控制到構造函數之前賦值,離開構造函數就沒有機會賦值了.

?record的本質就是一個語法糖,編譯器為我們做了很多事,這是我反編譯Person類的結果.本質還是一個類.

// Decompiled with JetBrains decompiler
// Type: RecordStudy.Person
// Assembly: RecordStudy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 60251223-809A-4A03-A8DA-EDD2743C7E5A
// Assembly location: E:\DonetProjects\RecordStudy\bin\Debug\net8.0\RecordStudy.dll
// Local variable names from E:\DonetProjects\RecordStudy\bin\Debug\net8.0\RecordStudy.pdb
// Compiler-generated code is shownusing System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;namespace RecordStudy
{[NullableContext(1)][Nullable(0)]public class Person : /*[Nullable(0)]*/IEquatable<Person>{[CompilerGenerated][DebuggerBrowsable(DebuggerBrowsableState.Never)]private readonly int \u003CId\u003Ek__BackingField;[CompilerGenerated][DebuggerBrowsable(DebuggerBrowsableState.Never)]private readonly string \u003CName\u003Ek__BackingField;public Person(int Id, string Name){this.\u003CId\u003Ek__BackingField = Id;this.\u003CName\u003Ek__BackingField = Name;base.\u002Ector();}[CompilerGenerated]protected virtual Type EqualityContract{[CompilerGenerated] get{return typeof (Person);}}public int Id{[CompilerGenerated] get{return this.\u003CId\u003Ek__BackingField;}[CompilerGenerated] init{this.\u003CId\u003Ek__BackingField = value;}}public string Name{[CompilerGenerated] get{return this.\u003CName\u003Ek__BackingField;}[CompilerGenerated] init{this.\u003CName\u003Ek__BackingField = value;}}[CompilerGenerated]public override string ToString(){StringBuilder builder = new StringBuilder();builder.Append("Person");builder.Append(" { ");if (this.PrintMembers(builder))builder.Append(' ');builder.Append('}');return builder.ToString();}[CompilerGenerated]protected virtual bool PrintMembers(StringBuilder builder){RuntimeHelpers.EnsureSufficientExecutionStack();builder.Append("Id = ");builder.Append(this.Id.ToString());builder.Append(", Name = ");builder.Append((object) this.Name);return true;}[NullableContext(2)][CompilerGenerated][SpecialName]public static bool op_Inequality(Person left, Person right){return !Person.op_Equality(left, right);}[NullableContext(2)][CompilerGenerated][SpecialName]public static bool op_Equality(Person left, Person right){if ((object) left == (object) right)return true;return (object) left != null && left.Equals(right);}[CompilerGenerated]public override int GetHashCode(){return (EqualityComparer<Type>.Default.GetHashCode(this.EqualityContract) * -1521134295 + EqualityComparer<int>.Default.GetHashCode(this.\u003CId\u003Ek__BackingField)) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(this.\u003CName\u003Ek__BackingField);}[NullableContext(2)][CompilerGenerated]public override bool Equals(object obj){return this.Equals(obj as Person);}[NullableContext(2)][CompilerGenerated]public virtual bool Equals(Person other){if ((object) this == (object) other)return true;return (object) other != null && Type.op_Equality(this.EqualityContract, other.EqualityContract) && EqualityComparer<int>.Default.Equals(this.\u003CId\u003Ek__BackingField, other.\u003CId\u003Ek__BackingField) && EqualityComparer<string>.Default.Equals(this.\u003CName\u003Ek__BackingField, other.\u003CName\u003Ek__BackingField);}[CompilerGenerated]public virtual Person \u003CClone\u003E\u0024(){return new Person(this);}[CompilerGenerated]protected Person(Person original){base.\u002Ector();this.\u003CId\u003Ek__BackingField = original.\u003CId\u003Ek__BackingField;this.\u003CName\u003Ek__BackingField = original.\u003CName\u003Ek__BackingField;}[CompilerGenerated]public void Deconstruct(out int Id, out string Name){Id = this.Id;Name = this.Name;}}
}

現在我們的Person類的兩個屬性都是只讀的了,但是萬一我們還有需求要添加一個可讀可寫的屬性,也有辦法.

只需要如下再添加一個屬性,同時觀察Tostring方法,雖然NickName屬性特殊一點,但是并沒有被Tostring方法忘記,Equals方法也是同理.

當然不僅是再添加屬性,還能添加構造函數

注意:

record聲明的類也是普通的類,變量的賦值也是引用的傳遞.

如何深拷貝一個對象呢?可以使用with

with一個給到另一個對象即完成了深拷貝. 同時如果你想改一些值也是可以的.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/79042.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/79042.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/79042.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

實時數倉dim層設計的一些疑惑點

0.dim層組件的選擇 dim層存儲要求&#xff1a;需要滿足永久存儲&#xff08;需要長期保存歷史數據&#xff09;和支持根據主鍵查詢單條數據明細&#xff0c;所以排除Kafka&#xff08;時效短&#xff09;&#xff1b; 候選框架&#xff1a;MySQL、Redis、Hive、Doris、HBase …

模型之FIM(Fill-In-the-Middle)補全

文章目錄 模型之FIM(Fill-In-the-Middle)補全什么是FIM(Fill-In-the-Middle)FIM 的工作原理FIM 示例:代碼補全場景常見模型fim 測試deepseek fim阿里completions 接口要判斷模型是否支持特定的特殊標記**1. 模型可以自動推斷生成區域****2. `suffix` 是可選的****3. 模型的…

使用CubeMX新建DMA工程——存儲器到外設模式

目錄 1、新建板級支持包 1、usart.c: 2、修改的地方&#xff1a;在usart.c中添加了這些 3、usart.h: 4、在usart.h中添加了這些&#xff1a; 5、dma.c: 6、dma.h: 2、修改main.c文件 1、在main.c文件中添加頭文件 2、添加外部變量聲明 3、添加簡單延時函數 4、添加…

el-transfer穿梭框數據量過大的解決方案

一&#xff1a;背景 我們這個穿梭框獲取的是項目的全量數據&#xff0c;在左邊大概有5000條&#xff0c;自己測試了一下5000條數據的效果&#xff0c;發現異常的卡頓&#xff0c;本來打算像el-select一樣去解決的&#xff08;只顯示一部分&#xff0c;在搜索的時候去全量搜索&a…

2025年- H17-Lc125-73.矩陣置零(矩陣)---java版

1.題目描述 2.思路 &#xff08;1&#xff09;計算矩陣的行數 &#xff08;2&#xff09;計算矩陣的列數 &#xff08;3&#xff09;設計一個行列的bool數組 &#xff08;4&#xff09;遍歷矩陣&#xff08;二維數組&#xff09;&#xff0c;如果遇到元素0&#xff0c;則把…

Qt二維碼demo

使用QZXing庫生成的二維碼demo 運行結果 實現代碼 c文件 #include "mainwindow.h" #include "ui_mainwindow.h" #include "src/myqrcodeheader.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui-&…

怪物獵人:世界-冰原10000+mod整合包5月最新更新!

700A大全套精美服裝 800M大全套精美服裝 3月31日更新 新增 新武器 新特效MOD 當前共計5800MOD整合包 好看的發型mod 實用的功能mod 炫酷的武器mod 新服裝新特效新武器實用模組美化&#xff0c;等。 1月14日更新 新增皮膚MOD 500 當前共計2000MOD 1月16日更新 新增超…

華納云:centos如何實現JSP頁面的動態加載

JSP(JavaServer Pages)作為Java生態中常用的服務器端網頁技術&#xff0c;具有動態內容生成、可擴展性強、與Java無縫結合等優勢。 而CentOS作為一款穩定、高效、安全的Linux服務器操作系統&#xff0c;非常適合部署JSP應用。 想要讓JSP頁面實現動態更新加載&#xff0c;避免…

gradle-tasks.register(‘classesJar‘, Jar)解析

在使用gradle作為構建工具的android或者java web項目中&#xff0c;我們經常能遇到以下格式 tasks.register(classesJar, Jar) {from "$buildDir/intermediates/javac/release/classes" // 假設使用 release 構建變體 }artifact sourcesJar使用偽代碼解釋 class Cu…

數據處理1

一、常用數據處理模塊Numpy Numpy常用于高性能計算&#xff0c;在機器學習常常作為傳遞數據的容器。提供了兩種基本對象&#xff1a;ndarray、ufunc。 ndarray具有矢量算術運算和復雜廣播能力的快速且節省空間的多維數組。 ufunc提供了對數組快速運算的標準數學函數。 ndar…

電力市場的交易品種

雙邊交易&#xff08;Bilateral Trading&#xff09; 定義&#xff1a;是電力市場中最基本的交易方式之一&#xff0c;指具備市場交易資格的買方和賣方&#xff0c;通過自主協商、雙邊協商的形式&#xff0c;確定交易電量、交易價格、交割曲線等交易要素&#xff0c;并簽訂中長…

uniapp 實現時分秒 分別倒計時

效果 <view class"issue-price-countdown"> <CountDown :endTimestamp"1745996085000"></CountDown> </view> 引入組件 import CountDown from /components/CountDown.vue; <template> <view class&qu…

從CRUD到復雜業務:AI自動生成電商優惠券疊加邏輯(新手救星指南)

在 Java 編程的廣闊天地中,據統計,高達 80% 的新手會在業務邏輯編寫環節陷入困境。業務邏輯作為軟件系統的核心靈魂,承載著從用戶需求到代碼實現的關鍵轉化過程,為何卻成為新手難以逾越的 “鴻溝”?飛算 JavaAI 的出現,又將如何打破這一僵局? 一、Java 新手卡在業務邏輯的根…

23頁PDF | 數據治理實施方案 :規劃、執行、評價、改進四步走的管控模式

在當今數字化時代&#xff0c;數據已經成為企業和組織的核心資產之一。然而&#xff0c;隨著數據量的不斷增長和數據來源的日益多樣化&#xff0c;數據治理變得愈發重要。有效的數據治理能夠確保數據的質量、安全和合規性&#xff0c;提升數據的價值和利用效率。那么&#xff0…

curl詳解

curl 是一個常用的命令行工具&#xff0c;用于發送 HTTP 請求&#xff0c;支持包括 GET、POST、PUT、DELETE 等在內的多種 HTTP 方法。它非常適合用來測試 API、下載文件、與后端服務進行交互等。接下來&#xff0c;我會詳細講解 curl 的基本用法以及常見的應用場景。 &#x…

Win11安裝Ubuntu20.04簡記

寫在前面 之前裝的22.04&#xff0c;不穩定&#xff0c;把22.04卸載了&#xff0c;重新安裝20.04系統。這里主要把卸載和安裝的過程中參考到的博客在這記錄一下。 卸載ubuntu系統參考的博文 卸載參考博文1 卸載參考博文2 Ubuntu20.04安裝參考博文 安裝參考博文1 安裝參考博…

云原生 | K8S中數據存儲之StorageClass

在一個大規模的Kubernetes集群里,可能有成千上萬個PVC,這就意味著運維人員必須實現創建出這個多個 PV,此外,隨著項目的需要,會有新的PVC不斷被提交,那么運維人員就需要不斷的添加新的,滿足要求的PV,否 則新的Pod就會因為PVC綁定不到PV而導致創建失敗。而且通過 PVC 請求到一定的…

基于Hadoop大數據技術音樂推薦系統數據分析與可視化(基于Spark和Hive的音樂推薦系統數據分析與可視化)基于Python的音樂推薦系統數據分析與可視化

基于Hadoop大數據技術音樂推薦系統數據分析與可視化&#xff08;基于Spark和Hive的音樂推薦系統數據分析與可視化&#xff09;基于Python的音樂推薦系統數據分析與可視化 1. 開發工具和實現技術 Pycharm, Python3.7&#xff0c;Django框架&#xff0c;Hadoop&#xff0c;Spar…

podman/docker國內可用的docker鏡像源(2025-05)

一、添加Docker國內鏡像 1、修改 /etc/docker/daemon.json 設置 registry mirror&#xff0c;具體命令如下: sudo vim /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1ms.run","https://docker.xuanyuan.me",&q…

【Java ee初階】多線程(4)

一、java是怎么做到可重入的 java中&#xff0c;通過synchronized進行加鎖&#xff0c;指定一個&#xff08;&#xff09;包含了一個鎖對象。&#xff08;鎖對象本身是一個啥樣的對象&#xff0c;這并不重要&#xff0c;重點關注鎖對象是不是同一個對象&#xff09; 后面搭配…