C# 中記錄(Record)詳解

從C#9.0開始,我們有了一個有趣的語法糖:記錄(record)  

  為什么提供記錄?

  開發過程中,我們往往會創建一些簡單的實體,它們僅僅擁有一些簡單的屬性,可能還有幾個簡單的方法,比如DTO等等,但是這樣的簡單實體往往又很有用,我們可能會遇到一些情況:

  比如想要克隆一個新的實體而不是簡單的引用傳遞  

  比如想要簡單的比較屬性值是否都一致,

  比如在輸出,我們希望得到內部數據結構而不是簡單的甩給我們一個類型名稱

  其實,這說的有些類似結構體的一些特性,那為什么不直接采用結構體來實現呢?這是因為結構體有它的一些不足:  

    1、結構體不支持繼承2、結構體是值傳遞過程,因此,這意味著大量的結構體擁有者相同的數據,但是占用這不同內存3、結構體內部相等判斷使用ValueType.Equals方法,它是使用反射來實現,因此性能不快

  而引用類型記錄,正好彌補了這些缺陷。

  在C#9.0中,我們使用record關鍵字聲明一個記錄類型,它只能是引用類型:  

    public record Animal;

  從C#10開始,我們不僅有引用類型記錄,還有結構體記錄:

//使用record class聲明為引用類型記錄,class關鍵字是可選的,當缺省時等價于C#9.0中的record用法public record Animal;//等價于public record class Animal;//使用record struct聲明為結構體類型記錄public record struct Animal;//也可使用readonly record struct聲明為只讀結構體類型記錄public readonly record struct Animal;

  至于它們是什么,區別上和普通class、struct有什么不一樣,我們慢慢道來

  引用類型記錄

  引用類型記錄不是一種新的類型,它是class用法的一個新用法,新的語法糖,也就是說record class是引用類型(這個在C#9.0中沒有record class的寫法,直接使用record)。

  先看看引用類型記錄是什么樣子的,首先是無構造參數的記錄

  //無構造參數,無其它方法屬性等public record Animal;//實例化var animal = new Animal();

?  在編譯時,會生成對應的class,大致等價于下面的例子:  

 public class Animal : IEquatable<Animal>{public Animal() { }protected Animal(Animal original) { }protected virtual Type EqualityContract => typeof(Animal);public virtual Animal <Clone>$() => new Animal(this);public virtual bool Equals(Animal? other) => (other != null) && (this.EqualityContract == other.EqualityContract);public override bool Equals(object obj) => this.Equals(obj as Animal);public override int GetHashCode() => EqualityComparer<Type>.Default.GetHashCode(this.EqualityContract);protected virtual bool PrintMembers(StringBuilder builder) => false;public override string ToString(){StringBuilder builder = new StringBuilder();builder.Append("Animal");builder.Append(" { ");if (this.PrintMembers(builder)){builder.Append(" ");}builder.Append("}");return builder.ToString();}public static bool operator ==(Animal r1, Animal r2) => (r1 == r2) || ((r1 != null) && r1.Equals(r2));public static bool operator !=(Animal r1, Animal r2) => !(r1 == r2);}

  可以看到,除了幾個相比較的方法,那么這個記錄的作用幾乎等價于object了!這里有一個<Clone>$(),方法,這是編譯器生成的,作用后面再解釋。

  再看看有構造參數的記錄:

 //有構造參數,無其它方法屬性等public record Person(string Name, int Age);//實例化var person = new Person("zhangsan", 1);

  注:上面的定義可能會報錯:

  

  據說這是VS2019的一個小BUG,因為記錄會生成 init setter,解決辦法是添加一個命名空間是System.Runtime.CompilerServices,名稱是IsExternalInit類就行了:  

  namespace System.Runtime.CompilerServices{class IsExternalInit{}}

  有構造參數的記錄在編譯時,會生成對應的class,大致等價于下面的例子:

 public class Person : IEquatable<Person>{public Person(string Name, int Age){this.Name = Name;this.Age = Age;}protected Person(Person original){this.Name = original.Name;this.Age = original.Age;}protected virtual Type EqualityContract => typeof(Person);public string Name { get; init; }public int Age { get; init; }public virtual Person <Clone>$() => new Person(this);public void Deconstruct(out string Name, out int Age) => (Name, Age) = (this.Name, this.Age);public virtual bool Equals(Person? other) => (other != null) && (this.EqualityContract == other.EqualityContract) &&EqualityComparer<string>.Default.Equals(this.Name, other.Name) && EqualityComparer<int>.Default.Equals(this.Age, other.Age);public override bool Equals(object obj) => this.Equals(obj as Person);public override int GetHashCode() => (((EqualityComparer<Type>.Default.GetHashCode(this.EqualityContract) * -1521134295) + EqualityComparer<string>.Default.GetHashCode(this.Name)) * -1521134295) + EqualityComparer<int>.Default.GetHashCode(this.Age);protected virtual bool PrintMembers(StringBuilder builder){builder.Append("Name");builder.Append(" = ");builder.Append(this.Name);builder.Append(", ");builder.Append("Age");builder.Append(" = ");builder.Append(this.Age.ToString());return true;}public override string ToString(){StringBuilder builder = new StringBuilder();builder.Append("Person");builder.Append(" { ");if (this.PrintMembers(builder)){builder.Append(" ");}builder.Append("}");return builder.ToString();}public static bool operator ==(Person r1, Person r2) => (r1 == r2) || ((r1 != null) && r1.Equals(r2));public static bool operator !=(Person r1, Person r2) => !(r1 == r2);}

  可以看到,相比無構造參數的記錄,有構造參數的記錄將構造參數生成了屬性(setter是init),而且Equals、GetHashCode、ToString等方法重載都有這幾個屬性參與。

  除此之外,還生成了一個Deconstruct方法,因此,有構造參數的記錄就具有解構能力。另外,這里也同樣生成了一個<Clone>$方法。

  接下來看看記錄的這些屬性和方法:

  1、構造函數和屬性

  記錄會根據給定的參數生成一個構造函數,同時為每一個構造參數生成一個屬性(為了規范,參數應采用匈牙利命名法,首字符大寫),比如上面的Animal記錄,等價于:  

 public class Animal : IEquatable<Animal>{public Animal(string Name, int Age){this.Name = Name;this.Age = Age;}public string Name { get; init; }public int Age { get; init; }//其他方法屬性}

  這里的屬性的setter是init,也就是說記錄具有不可變性,記錄一旦初始化完成,那么它的屬性值將不可修改(可以通過反射修改)。

  另外,記錄允許我們自定義構造方法和屬性,但是需要遵循:  

    1、記錄在編譯時會根據構造參數生成一個默認的構造函數,默認構造函數不能被覆蓋,如果有自定義的構造函數,那么需要使用this關鍵字初始化這個默認的構造函數2、記錄中可以自定義屬性,自定義屬性名可以與構造參數名重名,也就是說自定義屬性可以覆蓋構造參數生成的屬性,此時對應構造參數將不起任何作用,但是我們可以通過屬性指向這個構造參數來自定義這樣一個屬性

  比如:    

    public record Person(string Name, int Age){//自定義構造函數需要使用this初始化默認構造函數public Person(string Name) : this(Name, 18){ }//覆蓋構造參數中的Age,屬性不用是init,可以自定義,public也可以改成internal等等internal int Age { get; set; } = Age;//這個賦值很重要,如果沒有,構造函數中的參數值將不會給到屬性,也就是說構造函數中的Age不起任何作用//額外的自定義屬性public DateTime Birth { get; set; }}//等價于public class Person : IEquatable<Person>{public Person(string Name) : this(Name, 18){ }public Person(string Name, int Age){this.Name = Name;this.Age = Age;}public string Name { get; init; }internal int Age { get; set; }//Age改變//額外的自定義屬性public DateTime Birth { get; set; }//其他方法及屬性}

  從上面可以看到,雖然記錄具有不可變性,但是我們可以通過自定義屬性來覆蓋原來的行為,讓其屬性變為可修改的,Age屬性有原來的public和init變為internal和set。

  此外,在創建一個記錄時,可以給構造參數指定一些特性標識,在編譯時會用這些特性給到生成的對應屬性,如:  

public record Person([property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("age")] int Age);//等價于public class Person : IEquatable<Person>{[JsonPropertyName("name")]public string Name { get; set; }[JsonPropertyName("age")]public int Age { get; set; }//其他方法及屬性}

  其中property表示特性加在屬性上,field表示特性加在字段上,param表示特性加在構造函數的參數上

  2、記錄可以解構

  上面的例子可以看到,每個記錄,在編譯時會針對構造參數生成一個Deconstruct 方法,因此記錄天生就支持解構:  

 Person person = new Person("zhangsan", 21);var (name, age) = person;Console.WriteLine($"name={name},age={age}");//name=zhangsan,age=21

  注:解構只針對默認構造函數的構造參數,不計算自定義的屬性和構造函數,如果需要,我們還可以重載自己的解構Deconstruct方法

  3、記錄可以繼承  

  記錄可繼承,但是需要遵循:  

    1、一條記錄可以從另一條記錄繼承,但不能從一個類中繼承,一個類也不能從一個記錄繼承2、繼承的子記錄必須聲明父記錄中各參數

  例如:  

 public record Person(string Name, int Age);public record Teacher(string Phone, int Age, string Name) : Person(Name, Age);public record Student(string Grade, int Age, string Name) : Person(Name, Age);

  4、值相等性

  值相等性一般是值類型的一個概念,而記錄是引用類型,要實現值相等性,主要通過三個方面來實現:

  • 重寫Object的Equals和GetHashCode方法
  • 重寫運算符?==?和?!=
  • 實現了IEquatable<T>接口

  重寫Object的Equals方法和重寫運算符 == 、!=很好理解,因為引用類型在使用Equals方法或者運算符 == 、!=作判斷時,是根據對象是否是同一個對象的引用而返回true或者false,例如: 

  public record Person(string Name, int Age);static void Main(string[] args){//一般引用類型var exception1 = new Exception();var exception2 = exception1;Console.WriteLine(exception1.Equals(exception2));//trueConsole.WriteLine(exception1 == exception2);//trueConsole.WriteLine(exception1.Equals(new Exception()));//falseConsole.WriteLine(exception1 == new Exception());//false//記錄var person1 = new Person("zhangsan", 18);var person2 = person1;Console.WriteLine(person1.Equals(person2));//trueConsole.WriteLine(person1 == person2);//trueConsole.WriteLine(person1.Equals(new Person("zhangsan", 18)));//trueConsole.WriteLine(person1 == new Person("zhangsan", 18));//true}

  對于實現了IEquatable<T>接口,是為了讓記錄在泛型集合中,如Dictionary<TKey,TValue>, List<T>等,在使用Contains, IndexOf, LastIndexOf, Remove等方法時可以像string,int,bool等類型一樣對待,例如:  

public record Person(string Name, int Age);static void Main(string[] args){//一般引用類型List<Exception> exceptions = new List<Exception>() { new Exception() };Console.WriteLine(exceptions.IndexOf(new Exception()));//-1Console.WriteLine(exceptions.Contains(new Exception()));//falseConsole.WriteLine(exceptions.Remove(new Exception()));//false//記錄List<Person> persons = new List<Person>() { new Person("zhangsan", 18) };Console.WriteLine(persons.IndexOf(new Person("zhangsan", 18)));//0Console.WriteLine(persons.Contains(new Person("zhangsan", 18)));//trueConsole.WriteLine(persons.Remove(new Person("zhangsan", 18)));//true}

  換句話說,雖然記錄是引用類型,但是我們應該將記錄按值類型一樣去使用。

  注意:  

    1、實現的IEquatable<T>接口的Equals方法和重寫的GetHashCode方法中使用的屬性不僅僅是構造參數對應的屬性,還包含自定義的屬性、繼承的屬性(包括public,internal,protected,private,但是需要有get獲取器)2、無論是重寫Object的Equals方法,還是重寫運算符 == 和 !=,最終都是調用實現的IEquatable<T>接口的Equals方法

  雖然記錄的值相等性很好用,但是這有個問題,因為記錄可繼承,那么如果父子記錄的屬性值一樣,如果判定他們相同顯然不合理,因此編譯時額外生成了一個EqualityContract屬性:  

    1、EqualityContract屬性指向當前的記錄類型(Type),使用protected修飾2、如果記錄沒有從其它記錄繼承,那么EqualityContract屬性會帶有virtual修飾,否則將會使用override重寫3、如果記錄指定為sealed,即不可派生,那么EqualityContract屬性會帶有sealed修飾

  為了保證父子記錄的差異性,在實現的IEquatable<T>接口的Equals方法中,處理判斷屬性值相同外,還會判斷記錄類型是否一致,即EqualityContract屬性

  那如果說,我們需要只考慮屬性值,而不考慮類型時,需要判斷他們相等,這時只需要重寫EqualityContract屬性,將它指向同一個Type即可。

  此外,可以自定義Equals方法,這樣編譯時就不會生成Equals方法。

  5、非破壞性變化:with

  因為記錄是引用類型,而屬性的setter是init,因此當我們需要克隆一個記錄時就出現困難了,我們可以通過自定義屬性來修改setter來實現,但這不是記錄的初衷。

  記錄可以使用with關鍵字來實現非破壞性的變化:  

  public record Person(string Name, DateTime Birth, int Age, string Phone, string Address);static void Main(string[] args){//初始化了一個對象Person person = new("zhangsan", new DateTime(1999, 1, 1), 22, "13987654321", "中國");//如果想改下地址,因為記錄的不可變性,不能直接使用屬性修改//person.Address = "中國深圳";//報錯//方法一:可以重新初始化,但是不方便person = new(person.Name, person.Birth, person.Age, person.Phone, "中國深圳");//方法二:可以使用with關鍵字person = person with { Address = "中國深圳" };//可以使用with關鍵字克隆一個對象var clone = person with { };Console.WriteLine(clone == person);//trueConsole.WriteLine(ReferenceEquals(clone, person));//false}

?  使用with關鍵字時會先調用<Clone>$()方法來創建一個對象,然后對這個對象進行指定屬性的初始化,這就是最開始的例子中<Clone>$()方法的作用:  

 person = person with { Address = "中國深圳" };//在編譯后等價于var temp=person.<Clone>$();temp.Address = "中國深圳";person = temp;

  在寫代碼時,我們當然不能顯式的調用<Clone>$()方法,因為名稱不合法(它是編譯器生成的),<Clone>$()方法其實就是調用一個構造函數來實現初始化的,這表示我們可以通過自定義或者重寫這個構造函數來實現我們自己的邏輯:  

 public class Person : IEquatable<Person>{protected Person(Person original){this.Name = original.Name;this.Age = original.Age;}public virtual Person <Clone>$() => new Person(this);//其他方法屬性}

  注意,傳入構造函數的參數是原始對象,然后使用原始對象中的屬性值來進行初始化,如果屬性值是一個引用類型,那么它將進行淺復制過程。

  注:這里with用法針對引用類型記錄,值類型記錄的with參考后文

  6、內置格式化

  記錄還重寫了ToString,可以方便查看,輸出格式默認是:  

    記錄類型 { 屬性名1 = 屬性值1, 屬性名2 = 屬性值2, ...}

  例如:  

  public record Person(string Name, int Age);static void Main(string[] args){//初始化了一個對象Person person = new("zhangsan", 22);Console.WriteLine(person);//輸出:Person { Name = zhangsan, Age = 22 }}

  編譯器還合成了一個PrintMembers方法,如果我們有自己提供PrintMembers方法,編譯器就不會合成了,所以如果我們想要實現自己的格式化,只需要實現自己的PrintMembers方法,而不用重寫ToString方法。  

  值類型記錄

  注:值類型記錄只針對C#10及以后的版本有效

  值類型記錄也就是結構體記錄,大體上,值類型記錄與引用類型記錄的區別,就跟值類型與引用類型的區別差不多,所以具體不介紹,可以參考上面引用類型的介紹,這里只具體介紹它們的區別。

  值類型記錄又分為兩種:record struct和readonly record struct,這里結合record class來看看它們的區別:

  比如有三個record:  

public record class Point1(double X, double Y);
public readonly record struct Point2(double X, double Y);
public record struct Point3(double X, double Y);

  這里Point1是record class,Point2是readonly record struct,Point3是record struct,經過編譯,它們等價于下面的三個類和結構體(方法體去掉了,具體可參考上面引用類型記錄):  

  可以看到,這三種類型的記錄主要有共同點有:  

    1、對記錄的參數,分別生成了屬性2、生成了一個包含記錄所有屬性的構造函數3、重寫了 Object.Equals(Object)方法和Object.GetHashCode()方法4、實現了System.IEquatable<T>接口5、實現了==和!=運算操作6、實現了Deconstruct方法而實現解構操作7、重寫了Object.ToString()方法,以及創建了一個PrintMembers用于序列化(但是PrintMembers有些許區別)

  共同點沒什么好說的,參考上面引用類型介紹就可以了,接下來說說不同點:

  1、record class和readonly record struct生成的屬性是get和init標識,也就是說它們的對象是只讀的,而record struct生成的屬性是get和set標識,也就是說它的對象是可讀可寫的

  例如:

    var point1 = new Point1(1, 2);point1.X = 2;//報錯var point2 = new Point2(1, 2);point2.X = 2;//報錯var point3 = new Point3(1, 2);point3.X = 2;//編譯通過

  2、在構造函數上,record class會生成兩個構造函數:一個是protected修飾,用于<Clone>$()方法克隆一個對象,一個public修飾,包含所有的構造參數,而readonly record struct和record struct只包含一個public修飾,包含所有的構造參數的構造函數,但是因為它們的本質還是結構體,因此默認會有一個空構造函數,因此在創建時有區別:  

 //創建時需要指定所有的參數,protected修飾的構造函數不能在記錄及子記錄外使用var point1 = new Point1(1, 2);//除了可以指定所有參數的構造函數,還可以使用空構造函數初始化var point2 = new Point2(1, 2);point2 = new Point2();var point3 = new Point3(1, 2);point3 = new Point3();

  3、record class類型記錄會生成一個<Clone>$()方法,它通過調用一個protected的構造函數來克隆出一個新的引用對象,而我們可以通過自定義或者重寫這個protected的構造函數的構造函數來實現我們自己業務邏輯。

  其實這個<Clone>$()方法是在with關鍵字中使用的:  

    var point1 = new Point1(1, 2);var point = point1 with { X = 2 };//等價于var point1 = new Point1(1, 2);var point = point1.<Clone>$();point.X = 2;//注:編譯時給point.X賦值會報錯(因為init),這里只是說明

  而對于readonly record struct和record struct類型記錄,因為它們的本質是struct,天生是值復制的,因此就不需要這么一個<Clone>$()方法了,與此對應的是,結構體默認會有空構造函數(C#10)。

    var point2 = new Point2(1, 2);var point = point2 with { X = 2 };//等價于var point = point2;//struct是值復制過程point.X = 2;
 //注:編譯時給readonly record struct聲明的屬性賦值會報錯,而給record struct聲明的屬性賦值不會報錯,這里只是說明

  4、record struct,readonly record struct,record class都可以擁有自定義屬性,但是有些許區別

    1、record class按類中的屬性規則去定義2、record struct按結構體中的屬性規則去定義,此外,定義的屬性必須進行初始化3、readonly record struct按結構體中的屬性規則去定義,此外,定義的屬性必須進行初始化,而且定義的屬性只能是只讀的

  例如:  

  public record class Point1(double X, double Y){public double Z { get; set; }}public readonly record struct Point2(double X, double Y){public double Z { get; } = default;//必須初始化,此外readonly修飾所以只能只讀}public record struct Point3(double X, double Y){public double Z { get; set; } = default;//必須初始化}

  5、record struct,readonly record struct,record class都實現了System.IEquatable<T>接口,而且重寫了Object.Equals(Object)方法(本質上是通過System.IEquatable<T>接口來實現),但是record class中實現的Equals方法除了比較屬性以外,還會比較記錄的類型是否一致(即比較EqualityContract屬性,這一點可以參考上面介紹的引用類型記錄的值相等性部分),而對于record struct,readonly record struct,在編譯時,并沒有生成一個EqualityContract屬性,在實現的Equals方法也只是比較了屬性值,沒有比較類似是否一致。

  其實想想,結構體只能實現接口,而不能從另一個結構體派生,因此在在實現的Equals方法自然就沒有進行類型判斷的必要了。

  6、record struct,readonly record struct,record class都重寫了Object.ToString()方法,而且都是通過創建了一個PrintMembers方法來實現的,但是在PrintMembers方法上表現的行為不一致(這是一點細節,了解即可)。  

    1、如果記錄是結構體記錄(即record struct和readonly record struct),或者使用sealed關鍵字修飾,那么生成的PrintMembers方法是:private bool PrintMembers(StringBuilder builder);2、如果記錄沒有使用sealed關鍵字修飾,且記錄直接派生自Object(即沒有派生自一個父記錄),那么生成的PrintMembers方法是:protected virtual bool PrintMembers(StringBuilder builder);3、如果記錄派生自一個父記錄,那么生成的PrintMembers方法是:protected override bool PrintMembers(StringBuilder builder);

  總結

  記錄是一個語法糖,本質上還是class或者struct,它只編譯時生效,運行時并沒有記錄這個東西,此外,根據官網介紹,記錄不適合在EntityFrameworkCore中使用,畢竟它重寫了Equals方法和相等運算(==和!=),這樣可能會對EntityFrameworkCore的實體跟蹤機制造成影響。

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

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

相關文章

使用 CSS 實現透明效果

在 CSS 中&#xff0c;實現透明效果有幾種方法&#xff0c;具體使用哪種方法取決于具體需求。以下是一些常見的方法&#xff1a; 使用 opacity 屬性&#xff1a; opacity 屬性可以設置整個元素的透明度&#xff0c;包括其所有的子元素。 .transparent { opacity: 0.5; /* 0 表…

MS17-010(永恒之藍1.0)漏洞遠程控制win7系統操作實戰小白通俗易懂

1.準備環境win7操作系統&#xff08;被攻擊機&#xff09;以及kali系統&#xff08;攻擊機&#xff09;&#xff0c;kali使用msf工具進行攻擊。 2.打開kali終端&#xff0c;進入msf&#xff0c;輸入msfconsole然后等待啟動。 ┌──(root?kali-chifan)-[~] └─# msfconsole…

C語言:函數棧幀的創建和銷毀

目錄 1.什么是函數棧幀2.理解函數棧幀能解決什么問題3.函數棧幀的創建和銷毀的過程解析3.1 什么是棧3.2 認識相關寄存器和匯編指令3.3 解析函數棧幀的創建和銷毀過程3.3.1 準備環境3.3.2 函數的調用堆棧3.3.3 轉到反匯編3.3.4 函數棧幀的創建和銷毀 1.什么是函數棧幀 在寫C語言…

25/2/6 <機器人基礎> 運動學中各連桿的變換矩陣求法

變換矩陣 機器人通常包含多個關節和連桿&#xff0c;每個關節和連桿都有自己的局部坐標系。變換矩陣能夠將一個點或向量從一個坐標系轉換到另一個坐標系&#xff0c;從而實現對機器人各個部件位置和姿態的統一描述 變換矩陣能夠將復雜的運動分解為旋轉和平移的組合。通過矩陣乘…

AllData數據中臺核心菜單十二:數據同步平臺

&#x1f525;&#x1f525; AllData大數據產品是可定義數據中臺&#xff0c;以數據平臺為底座&#xff0c;以數據中臺為橋梁&#xff0c;以機器學習平臺為中層框架&#xff0c;以大模型應用為上游產品&#xff0c;提供全鏈路數字化解決方案。 ?奧零數據科技官網&#xff1a;…

【FPGA】 MIPS 12條整數指令 【3】

實現乘除 修改框架 EX&#xff1a;實現帶符號乘除法和無符號乘除法 HiLo寄存器&#xff1a;用于存放乘法和除法的運算結果。Hi、Lo為32bit寄存器。電路描述與實現RegFile思想一致 仿真 代碼 DataMem.v include "define.v"; module DataMem(input wire clk,input…

【原子工具】快速冪 快速乘

題冪算.一切即1 陰陽迭變積微著&#xff0c;疊浪層巒瞬息功 莫道浮生千萬事&#xff0c;元知萬象一歸宗 文章目錄 快速冪原始快速冪&#xff08;O(logn)&#xff09;二分遞歸形式非遞歸形式 模下意義的快速冪&#xff08;O(logn)&#xff09;二分遞歸形式非遞歸形式 快速乘龜速…

文件基礎IO

理解"文件" 1-1 狹義理解 文件在磁盤里磁盤是永久性存儲介質&#xff0c;因此文件在磁盤上的存儲是永久性的磁盤是外設&#xff08;即是輸出設備也是輸入設備&#xff09;磁盤上的文件 本質是對文件的所有操作&#xff0c;都是對外設的輸入和輸出簡稱IO 1-2 廣義理…

Unity 簡易的UI框架

核心內容 UIType.cs namespace MYTOOL.UI {/// <summary>/// UI層級/// </summary>public enum UILayer{/// <summary>/// 主界面層/// </summary>MainUI 0,/// <summary>/// 普通界面層/// </summary>NormalUI 1,/// <summary>/…

VUE2雙向綁定的原理

文章目錄 VUE2雙向綁定的原理1. 什么是雙向綁定2. 雙向綁定的原理2.1 ViewModel的重要作用2.2 雙向綁定的流程 3. 雙向綁定的實現3.1 data響應化處理3.2 Compile編譯3.3 依賴收集 VUE2雙向綁定的原理 1. 什么是雙向綁定 講雙向綁定先講單項綁定&#xff0c;啥叫單項綁定&…

4G核心網的演變與創新:從傳統到虛擬化的跨越

4G核心網 隨著移動通信技術的不斷發展&#xff0c;4G核心網已經經歷了從傳統的硬件密集型架構到現代化、虛擬化網絡架構的重大轉型。這一演變不僅提升了網絡的靈活性和可擴展性&#xff0c;也為未來的5G、物聯網&#xff08;LOT&#xff09;和邊緣計算等技術的發展奠定了基礎。…

云計算——AWS Solutions Architect – Associate(saa)1、什么是云,AWS介紹

什么是云? 什么是云? 云計算(cloud computing)是基于互聯網的相關服務的增加、使用和交付模式&#xff0c;通常涉及通過互聯網來提供動態易護展且經常是虛擬化的資源。云是網絡、互聯網的一種比喻說法。 簡單理解為&#xff1a;云是 共享資源&#xff0c;按需付費&#xff0…

HTML排版標簽、語義化標簽、塊級和行內元素詳解

目錄 前言 一、HTML中的排版標簽 1. 文本相關標簽 1.1 標題標簽 ~ 1.2 段落標簽 1.3 強調和加粗 1.4 換行標簽 1.5 水平線標簽 二、HTML中的語義化標簽 2.1 語義化標簽概述 2.2 常見的語義化標簽 示例&#xff08;核心代碼部分&#xff09;&#xff1a; 三、HTM…

【字節青訓營-7】:初探 Kitex 字節微服務框架(使用ETCD進行服務注冊與發現)

本文目錄 一、Kitex概述二、第一個Kitex應用三、IDL四、服務注冊與發現 一、Kitex概述 長話短說&#xff0c;就是字節跳動內部的 Golang 微服務 RPC 框架&#xff0c;具有高性能、強可擴展的特點&#xff0c;在字節內部已廣泛使用。 如果對微服務性能有要求&#xff0c;又希望…

【數學】矩陣、向量(內含矩陣乘法C++)

目錄 一、前置知識&#xff1a;向量&#xff08;一列或一行的矩陣&#xff09;、矩陣1. 行向量2. 列向量3. 向量其余基本概念4. 矩陣基本概念5. 關于它們的細節 二、運算1. 轉置&#xff08;1&#xff09;定義&#xff08;2&#xff09;性質 2. 矩陣&#xff08;向量&#xff0…

TCP/IP 郵件

TCP/IP 郵件 引言 在互聯網技術飛速發展的今天,電子郵件(Email)已成為人們日常工作和生活中不可或缺的通信工具。TCP/IP協議作為互聯網通信的基礎,為電子郵件的傳輸提供了強大的技術支持。本文將詳細介紹TCP/IP在電子郵件傳輸過程中的作用,以及相關的協議和實現方式。 …

離線安裝Appium Server

1、問題概述? 安裝Appium通常有兩種方式: 第一種:下載exe安裝包,這種是Appium Server GUI安裝方式,缺點是通過命令啟動不方便。 第二種:通過cmd安裝appium server,可以通過命令方式啟動,比較方便。 問題:在沒有外網的情況下,無法通過命令在cmd中安裝appium server…

設計模式六大原則和單例模式

設計模式 目的 實現可重用解決方案&#xff0c;構筑易維護、可擴展的軟件系統。 六大原則 單一職責&#xff1a; 類的職責單一&#xff0c;一個方法做一件事。 開閉原則&#xff1a; 拓展開放&#xff0c;修改關閉。 里氏替換&#xff1a; 父類能出現的地方&#xff0c;子…

淺嘗yolo11全程記錄1-準備環境+官網模型推理(個人備份)

準備工作&#xff08;虛擬環境、導入項目&#xff09; 安裝Anaconda 主要是為了創建和管理虛擬環境&#xff0c;在pycharm里按照項目里的requirments.txt安裝依賴的時候&#xff0c;使用虛擬環境會好很多&#xff08;我記得不用Anaconda也可以直接在pycharm的terminal里頭創建…

5.攻防世界 fileinclude

進入題目頁面如下 提示flag在flag.php ctrlu&#xff0c;查看源碼 給出了一段PHP代碼&#xff0c;進行代碼審計 <?php // 檢查是否開啟了錯誤顯示功能 if( !ini_get(display_errors) ) {// 如果沒有開啟&#xff0c;則將錯誤顯示功能設置為開啟狀態ini_set(display_error…