门户建设网站/排名优化公司
从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的实体跟踪机制造成影响。
相关文章:

C# 中记录(Record)详解
从C#9.0开始,我们有了一个有趣的语法糖:记录(record) 为什么提供记录? 开发过程中,我们往往会创建一些简单的实体,它们仅仅拥有一些简单的属性,可能还有几个简单的方法,比如DTO等等…...

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-autobackend.py
autobackend.py ultralytics\nn\autobackend.py 目录 autobackend.py 1.所需的库和模块 2.def check_class_names(names): 3.def default_class_names(dataNone): 4.class AutoBackend(nn.Module): 1.所需的库和模块 # Ultralytics 🚀 AGPL-3.0 License …...

Docker使用指南(一)——镜像相关操作详解(实战案例教学,适合小白跟学)
目录 1.镜像名的组成 2.镜像操作相关命令 镜像常用命令总结: 1. docker images 2. docker rmi 3. docker pull 4. docker push 5. docker save 6. docker load 7. docker tag 8. docker build 9. docker history 10. docker inspect 11. docker prune…...

Rust 变量特性:不可变、和常量的区别、 Shadowing
Rust 变量特性:不可变、和常量的区别、 Shadowing Rust 是一门以安全性和性能著称的系统编程语言,其变量系统设计独特且强大。本文将从三个角度介绍 Rust 变量的核心特性:可变性(Mutability)、变量与常量的区别&#…...

NFT Insider #167:Champions Tactics 角色加入 The Sandbox;AI 助力 Ronin 游戏生态
引言:NFT Insider 由 NFT 收藏组织 WHALE Members、BeepCrypto 联合出品, 浓缩每周 NFT 新闻,为大家带来关于 NFT 最全面、最新鲜、最有价值的讯息。每期周报将从 NFT 市场数据,艺术新闻类,游戏新闻类,虚拟…...

鹧鸪云无人机光伏运维解决方案
在新能源产业蓬勃发展的当下,光伏电站作为清洁能源供应的关键一环,其稳定运行和高效运维至关重要。随着光伏电站规模持续扩大,数量不断增加,传统人工巡检方式的弊端日益显著。人工巡检不仅效率低、人力和时间成本高,而…...

NeuralCF 模型:神经网络协同过滤模型
实验和完整代码 完整代码实现和jupyter运行:https://github.com/Myolive-Lin/RecSys--deep-learning-recommendation-system/tree/main 引言 NeuralCF 模型由新加坡国立大学研究人员于 2017 年提出,其核心思想在于将传统协同过滤方法与深度学习技术相结…...

【前端】【Ts】【知识点总结】TypeScript知识总结
一、总体概述 TypeScript 是 JavaScript 的超集,主要通过静态类型检查和丰富的类型系统来提高代码的健壮性和可维护性。它涵盖了从基础数据类型到高级类型、从函数与对象的类型定义到类、接口、泛型、模块化及装饰器等众多知识点。掌握这些内容有助于编写更清晰、结…...

JAVA架构师进阶之路
JAVA架构师进阶之路 前言 苦于网络上充斥的各种java知识,多半是互相抄袭,导致很多后来者在学习java知识中味同嚼蜡,本人闲暇之余整理了进阶成为java架构师所必须掌握的核心知识点,后续会不断扩充。 废话少说,直接上正…...

掌握@PostConstruct与@PreDestroy,优化Spring Bean的初始化和销毁
在Spring中,PostConstruct和PreDestroy注解就像是对象的“入职”和“离职”仪式。 1. PostConstruct注解:这个注解标记的方法就像是员工入职后的“岗前培训”。当一个对象(比如一个Bean)被Spring容器创建并注入依赖后,…...

Java设计模式:行为型模式→状态模式
Java 状态模式详解 1. 定义 状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变其行为。状态模式通过将状态需要的行为封装在不同的状态类中,实现对象行为的动态改变。该模式的核心思想是分离不同状态…...

景联文科技:专业数据采集标注公司 ,助力企业提升算法精度!
随着人工智能技术加速落地,高质量数据已成为驱动AI模型训练与优化的核心资源。据统计,全球AI数据服务市场规模预计2025年突破200亿美元,其中智能家居、智慧交通、医疗健康等数据需求占比超60%。作为国内领先的AI数据服务商,景联文…...

ES面试题
1、Elasticsearch的基本构成: (1)index 索引: 索引类似于mysql 中的数据库,Elasticesearch 中的索引是存在数据的地方,包含了一堆有相似结构的文档数据。 (2)type 类型:…...

LabVIEW2025中文版软件安装包、工具包、安装教程下载
下载链接:LabVIEW及工具包大全-三易电子工作室http://blog.eeecontrol.com/labview6666 《LabVIEW2025安装图文教程》 1、解压后,双击install.exe安装 2、选中“我接受上述2条许可协议”,点击下一步 3、点击下一步,安装NI Packa…...

算法与数据结构(合并K个升序链表)
思路 有了合并两个链表的基础后,这个的一种方法就是可以进行顺序合并,我们可以先写一个函数用来合并两个链表,再在合并K个链表的的函数中循环调用它。 解题过程 解析这个函数 首先,可以先判断,如果a为空,…...

洛谷 P4552 [Poetize6] IncDec Sequence C语言
P4552 [Poetize6] IncDec Sequence - 洛谷 | 计算机科学教育新生态 题目描述 给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间 [l,r],使这个区间内的数都加 1 或者都减 1。 请问至少需要多少次操作才能使数列中的所有数都一样&#…...

保姆级教程Docker部署Zookeeper官方镜像
目录 1、安装Docker及可视化工具 2、创建挂载目录 3、运行Zookeeper容器 4、Compose运行Zookeeper容器 5、查看Zookeeper运行状态 6、验证Zookeeper是否正常运行 1、安装Docker及可视化工具 Docker及可视化工具的安装可参考:Ubuntu上安装 Docker及可视化管理…...

javaEE-6.网络原理-http
目录 什么是http? http的工作原理: 抓包工具 fiddler的使用 HTTP请求数据: 1.首行:编辑 2.请求头(header) 3.空行: 4.正文(body) HTTP响应数据 1.首行:编辑 2.响应头 3.空行: 4.响应正文…...

【戒抖音系列】短视频戒除-1-对推荐算法进行干扰
如今推荐算法已经渗透到人们生活的方方面面,尤其是抖音等短视频核心就是推荐算法。 【短视频的危害】 1> 会让人变笨,慢慢让人丧失注意力与专注力 2> 让人丧失阅读长文的能力 3> 让人沉浸在一个又一个快感与嗨点当中。当我们刷短视频时&#x…...

9.建造者模式 (Builder Pattern)
定义 建造者模式(Builder Pattern)是一种创建型设计模式,旨在将复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。该模式的核心思想是通过一步步地构建一个复杂的对象,每个步骤独立且可扩展&…...

OpenCV:特征检测总结
目录 一、什么是特征检测? 二、OpenCV 中的常见特征检测方法 1. Harris 角点检测 2. Shi-Tomasi 角点检测 3. Canny 边缘检测 4. SIFT(尺度不变特征变换) 5. ORB 三、特征检测的应用场景 1. 图像匹配 2. 运动检测 3. 自动驾驶 4.…...

Clion开发STM32时使用stlink下载程序与Debug调试
一、下载程序 先创建一个文件夹: 命名:stlink.cfg 写入以下代码: # choose st-link/j-link/dap-link etc. #adapter driver cmsis-dap #transport select swdsource [find interface/stlink.cfg]transport select hla_swdsource [find target/stm32f4x.…...

电脑开机键一闪一闪打不开
家人们谁懂啊!本来打算愉快地开启游戏时光,或者高效处理工作任务,结果按下电脑开机键后,它就只是一闪一闪的,怎么都打不开。相信不少朋友都遭遇过这种令人崩溃的场景,满心的期待瞬间化为焦急与无奈。电脑在…...

深度学习 Pytorch 基础网络手动搭建与快速实现
为了方便后续练习的展开,我们尝试自己创建一个数据生成器,用于自主生成一些符合某些条件、具备某些特性的数据集。 导入相关的包 # 随机模块 import random# 绘图模块 import matplotlib as mpl import matplotlib.pyplot as plt# 导入numpy import nu…...

Sqli-labs靶场实录(一):Basic Challenges
sqli-labs靶场实录:Basic Challenges sql手注基本流程Less-11.1探测注入点1.2判断字段数1.3判断回显位1.4提取数据库基本信息1.5拖取敏感数据 Less-2Less-3Less-4Less5爆表爆列名 Less6爆库爆表爆列名 Less7猜解数据库长度逐字符爆破数据库名 Less8爆库 Less9爆库 Less10Less11…...

2024最新版Node.js详细安装教程(含npm配置淘宝最新镜像地址)
一:Node.js安装 浏览器中搜索Nodejs,或直接用网址:Node.js — 在任何地方运行 JavaScript 建议此处下载长期支持版本(红框内): 开始下载,完成后打开文件: 进入安装界面,在此处勾选,再点击n…...

RK3568使用QT搭建TCP服务器和客户端
文章目录 一、让RK3568开发板先连接上wifi二、客户端代码1. `widget.h` 文件2. `widget.cpp` 文件**详细讲解**1. **`Widget` 类构造函数 (`Widget::Widget`)**2. **UI 布局 (`setupUI`)**3. **连接按钮的槽函数 (`onConnectClicked`)**4. **发送消息按钮的槽函数 (`onSendMess…...

Android学习20 -- 手搓App2(Gradle)
1 前言 昨天写了一个完全手搓的:Android学习19 -- 手搓App-CSDN博客 后面谷歌说不要用aapt,d8这些来搞。其实不想弄Gradle的,不过想着既然开始了,就多看一些。之前写过一篇Gradle,不过是最简单的编译,不涉…...

LeetCode - Google 大模型10题 第2天 Position Embedding(位置编码) 3题
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/145454489 在 Transformer 架构中,位置编码(Position Embedding) 是辅助模型理解序列中元素顺序的关键机制。绝对位置编码(Absolute P…...

PostgreSQL 数据库备份与还原
为了安全与数据共享等,创建好的数据库有时候需要备份操作和还原操作。数据库的备份与还原主要是三个命令:pg_dump、pg_dumpall 和 pg_restore 。 其中pg_dump用于备份单个数据库,它支持多种备份格式(SQL、自定义等)&a…...