当前位置: 首页 > news >正文

C# 9.0记录类型:解锁开发效率的魔法密码

一、引言:记录类型的神奇登场

在 C# 的编程世界中,数据结构就像是构建软件大厦的基石,其重要性不言而喻。然而,传统的数据结构定义方式,尤其是在处理简单的数据承载对象时,常常显得繁琐复杂。例如,当我们需要定义一个表示人员信息的类时,通常需要编写如下代码:

public class Person
{public string Name { get; set; }public int Age { get; set; }public Person(string name, int age){Name = name;Age = age;}
}

上述代码中,我们不仅要定义属性,还要编写构造函数来初始化这些属性。当项目中存在大量类似这样简单的数据结构时,代码量会迅速膨胀,可读性和维护性也会随之降低。

而 C# 9.0 带来的记录类型(Record Types),就像是一把神奇的钥匙,为我们打开了简化数据结构定义的大门。记录类型能够以一种简洁明了的方式定义数据结构,让代码变得更加优雅和高效。例如,使用记录类型来定义上述的Person结构,只需一行代码:

public record Person(string Name, int age);

仅仅这一行代码,就完成了属性定义、构造函数生成等一系列操作,极大地简化了开发流程。记录类型不仅能减少代码的编写量,还在相等性比较、对象克隆等方面提供了便捷的功能,让开发效率得到显著提升。接下来,就让我们深入探索 C# 9.0 记录类型的奥秘,看看它是如何施展这简化数据结构的 5 步魔法的吧。

二、第一步:极简定义,告别冗长构造

在 C# 传统的类定义方式中,当我们需要创建一个简单的数据结构类时,往往需要编写较多的代码。以定义一个表示学生信息的类Student为例,传统方式如下:

public class Student
{public string Name { get; set; }public int Age { get; set; }public string Major { get; set; }public Student(string name, int age, string major){Name = name;Age = age;Major = major;}
}

在这段代码中,我们不仅要定义Name、Age和Major这三个属性,还要编写构造函数来初始化这些属性。如果项目中有大量类似这样简单的数据结构类,代码量会显著增加,而且这些重复的代码会降低代码的可读性和维护性。

而在 C# 9.0 中,使用记录类型来定义相同的Student数据结构,代码变得极其简洁:

public record Student(string Name, int Age, string Major);

仅仅这一行代码,就完成了属性定义、构造函数生成等一系列操作。编译器会自动为这个记录类型生成一个构造函数,该构造函数接受Name、Age和Major作为参数,用于初始化相应的属性。不仅如此,记录类型还会自动生成ToString、Equals和GetHashCode等方法,这些方法都是基于记录类型的属性值来实现的。

例如,当我们创建Student记录类型的实例时,可以这样使用:

var student1 = new Student("Alice", 20, "Computer Science");
Console.WriteLine(student1);

上述代码创建了一个student1实例,并使用Console.WriteLine输出该实例。由于记录类型自动生成了ToString方法,所以输出结果会以一种易读的格式展示student1的属性值,大致如下:

Student { Name = Alice, Age = 20, Major = Computer Science }

通过对比传统类和记录类型的定义方式,我们可以明显看出记录类型在简化数据结构定义方面的强大优势。它减少了大量的样板代码,让开发者能够更专注于业务逻辑的实现,提高了开发效率 。

三、第二步:轻松克隆,“with” 关键字显神通

在传统的 C# 编程中,当我们需要克隆一个对象并对其属性进行修改时,往往需要编写较为繁琐的代码。例如,对于前面定义的Student类,如果要克隆一个Student对象并修改其年龄,可能需要这样实现:

public class Student
{public string Name { get; set; }public int Age { get; set; }public string Major { get; set; }public Student(string name, int age, string major){Name = name;Age = age;Major = major;}// 手动实现克隆方法public Student Clone(){return new Student(Name, Age, Major);}
}// 使用克隆方法并修改属性
var student1 = new Student("Bob", 21, "Mathematics");
var student2 = student1.Clone();
student2.Age = 22;

在上述代码中,我们不仅要手动编写Clone方法来实现对象的克隆,而且代码看起来比较冗长和繁琐。

而在 C# 9.0 的记录类型中,使用with关键字可以轻松地实现对象的克隆并修改属性。with关键字就像是一个神奇的 “复制修改器”,它会创建一个基于现有记录对象的副本,并且可以在创建副本的同时修改指定的属性值,而其他未指定修改的属性则保持不变 。这一特性不仅大大简化了代码,还保障了对象的不变性原则,即原始对象不会被修改,而是生成一个新的对象来承载修改后的状态。

还是以Student记录类型为例,使用with关键字进行对象克隆和属性修改的代码如下:

public record Student(string Name, int Age, string Major);// 使用with关键字克隆并修改属性
var student1 = new Student("Charlie", 22, "Physics");
var student3 = student1 with { Age = 23 };

在上述代码中,student1 with { Age = 23 }这行代码创建了一个student1的副本student3,并且将student3的Age属性修改为 23,而Name和Major属性则与student1保持一致。

如果Student记录类型中的属性使用了init访问器来实现不可变,with关键字同样适用,并且能更好地体现不可变对象的优势。例如:

public record Student(string Name, int Age, string Major)
{public string Name { get; init; } = Name;public int Age { get; init; } = Age;public string Major { get; init; } = Major;
};var student1 = new Student("David", 23, "Chemistry");
var student4 = student1 with { Age = 24 };

在这个例子中,Student记录类型的属性是不可变的,通过with关键字创建新对象并修改属性,既保证了数据的不可变性,又实现了灵活的数据更新。

通过对比可以看出,with关键字在记录类型中实现对象克隆和属性修改的操作上,比传统的类方式更加简洁、直观,大大提高了开发效率 。

四、第三步:智能相等,告别 equals 的烦恼

在传统的 C# 编程中,当我们需要判断两个对象是否相等时,往往需要手动重写Equals和GetHashCode方法。以之前定义的Student类为例,如果要实现基于属性值的相等性比较,代码如下:

public class Student
{public string Name { get; set; }public int Age { get; set; }public string Major { get; set; }public Student(string name, int age, string major){Name = name;Age = age;Major = major;}public override bool Equals(object obj){if (obj == null || GetType()!= obj.GetType()){return false;}Student other = (Student)obj;return Name == other.Name && Age == other.Age && Major == other.Major;}public override int GetHashCode(){return HashCode.Combine(Name, Age, Major);}
}

在上述代码中,我们需要手动编写Equals方法来比较两个Student对象的属性值是否相等,同时还需要编写GetHashCode方法来生成哈希码,以确保相等的对象具有相同的哈希码 。这不仅增加了代码的编写量,还容易出现错误。

而在 C# 9.0 的记录类型中,编译器会自动为记录类型生成基于属性值的相等性比较逻辑,包括Equals和GetHashCode方法 。这意味着我们无需手动重写这些方法,就可以直接使用==运算符或Equals方法来判断两个记录类型的实例是否相等。

例如,对于之前定义的Student记录类型:

public record Student(string Name, int Age, string Major);

我们可以这样进行相等性比较:

var student1 = new Student("Eve", 23, "Biology");
var student2 = new Student("Eve", 23, "Biology");
var student3 = new Student("Frank", 24, "Economics");Console.WriteLine(student1 == student2); // 输出True
Console.WriteLine(student1.Equals(student2)); // 输出True
Console.WriteLine(student1 == student3); // 输出False

在上述代码中,student1和student2具有相同的属性值,所以student1 == student2和student1.Equals(student2)都返回True;而student1和student3的属性值不同,所以student1 == student3返回False。

记录类型的这种自动相等性比较功能,在处理集合中的数据比较、去重等操作时非常有用。例如,在一个包含Student记录类型的集合中,判断两个Student是否相等时,直接使用==运算符即可:

var studentList = new List<Student>
{new Student("Grace", 22, "History"),new Student("Hank", 21, "Geography")
};var newStudent = new Student("Grace", 22, "History");
bool isExists = studentList.Any(s => s == newStudent);
Console.WriteLine(isExists); // 输出True

上述代码中,studentList.Any(s => s == newStudent)用于判断studentList集合中是否存在与newStudent相等的元素,由于记录类型的自动相等性比较功能,我们可以非常方便地实现这一操作。

通过对比可以看出,C# 9.0 记录类型的自动相等性比较功能,大大简化了代码,减少了开发人员的工作量,提高了开发效率 。

五、第四步:深度剖析,记录类型的魔法内幕

前面我们已经体验到了 C# 9.0 记录类型在简化数据结构定义、对象克隆和相等性比较方面的强大功能。接下来,让我们深入记录类型的内部,探索它背后的魔法机制,看看编译器是如何施展这些神奇的操作的。

编译器的魔法合成

当我们使用记录类型定义一个数据结构时,编译器会自动为我们合成一系列非常有用的方法 。以之前定义的Student记录类型为例:

public record Student(string Name, int Age, string Major);

编译器会为这个记录类型合成以下方法:

  1. 构造函数:编译器会生成一个主构造函数,其参数与记录类型定义时的属性参数一致。例如,对于上述Student记录类型,生成的构造函数类似于:
public Student(string Name, int Age, string Major)
{this.Name = Name;this.Age = Age;this.Major = Major;
}

这个构造函数用于初始化记录类型的属性。此外,编译器还会生成一个受保护的复制构造函数(如果记录类型不是密封的),用于创建记录的副本。例如:

protected Student(Student other)
{this.Name = other.Name;this.Age = other.Age;this.Major = other.Major;
}
  1. 相等性比较方法:记录类型会自动实现基于属性值的相等性比较,编译器会合成Equals和GetHashCode方法。Equals方法用于比较两个记录类型的实例是否相等,它会比较两个实例的所有属性值。例如:
public override bool Equals(object obj)
{if (obj == null || GetType()!= obj.GetType()){return false;}Student other = (Student)obj;return Name == other.Name && Age == other.Age && Major == other.Major;
}public override int GetHashCode()
{return HashCode.Combine(Name, Age, Major);
}

GetHashCode方法用于生成记录类型实例的哈希码,它会根据记录类型的所有属性值生成一个唯一的哈希码,以确保相等的对象具有相同的哈希码 。

\3. ToString 方法:编译器会合成一个ToString方法,用于返回记录类型实例的字符串表示形式。这个字符串表示形式会包含记录类型的所有属性名和属性值,以一种易读的格式展示。例如:

public override string ToString()
{return $"Student {{ Name = {Name}, Age = {Age}, Major = {Major} }}";
}
  1. 克隆方法:虽然我们在代码中看不到名为Clone的方法,但编译器会生成一个用于创建记录副本的方法。这个方法实际上是使用with表达式来实现的,它会创建一个新的记录实例,并将原始记录的属性值复制到新实例中,同时可以根据需要修改指定的属性值。例如,对于Student记录类型,编译器生成的克隆方法类似于:
public Student Clone()
{return this with { };
}
  1. Deconstruct 方法:对于位置记录(即使用简洁语法定义的记录类型,如public record Student(string Name, int Age, string Major);),编译器还会生成一个Deconstruct方法,用于将记录类型的实例解构为其组件属性。例如:
public void Deconstruct(out string Name, out int Age, out string Major)
{Name = this.Name;Age = this.Age;Major = this.Major;
}

这个Deconstruct方法可以方便地将记录类型的实例分解为多个变量,例如:

var student = new Student("Ivy", 24, "Psychology");
var (name, age, major) = student;
Console.WriteLine($"Name: {name}, Age: {age}, Major: {major}");

初始化逻辑与不可变性

记录类型的属性默认是不可变的,这是通过使用init访问器来实现的。例如,对于Student记录类型,其属性的定义实际上类似于:

public record Student
{public string Name { get; init; }public int Age { get; init; }public string Major { get; init; }public Student(string Name, int Age, string Major){this.Name = Name;this.Age = Age;this.Major = Major;}
}

使用init访问器意味着属性只能在对象初始化时被赋值,一旦对象创建完成,属性值就不能被修改。这种不可变性有助于确保数据的一致性和安全性,特别是在多线程环境中,不可变的数据结构可以避免数据竞争和不一致的问题 。

当我们使用new关键字创建记录类型的实例时,会调用编译器生成的构造函数来初始化属性。例如:

var student = new Student("Jack", 25, "Sociology");

在这个过程中,Name、Age和Major属性会被初始化为指定的值,并且之后不能被修改。如果尝试修改属性值,例如:

student.Age = 26; // 编译错误,因为Age属性是不可变的

编译器会报错,提示属性是只读的,无法进行修改。

模式匹配的支持

记录类型对模式匹配提供了很好的支持,这使得我们可以根据记录类型的结构和属性值来进行灵活的条件判断和处理。模式匹配是一种检查数据结构的方式,它允许我们根据数据的特定模式来执行不同的代码分支。在 C# 中,模式匹配最常见的形式是switch表达式。

以Student记录类型为例,我们可以使用模式匹配来根据学生的年龄进行不同的处理:

var student = new Student("Kathy", 22, "Engineering");
switch (student)
{case { Age: < 20 }:Console.WriteLine("This student is relatively young.");break;case { Age: >= 20 and < 25 }:Console.WriteLine("This student is in a typical college age range.");break;case { Age: >= 25 }:Console.WriteLine("This student may be a mature learner.");break;
}

在上述代码中,switch表达式根据student的Age属性值进行匹配,不同的模式对应不同的处理逻辑。这种方式使得代码更加简洁、直观,避免了繁琐的条件判断和类型转换 。

记录类型还支持在模式匹配中使用with表达式来创建临时的记录副本并进行匹配。例如:

var student = new Student("Leo", 23, "Business");
switch (student)
{case { Name: "Leo", Age: var age } with { Major: "Business" }:Console.WriteLine($"Leo is a business student and his age is {age}.");break;
}

在这个例子中,通过with表达式在模式匹配中创建了一个临时的记录副本,并且只关注Name为"Leo"、Age为任意值且Major为"Business"的情况,进一步展示了记录类型在模式匹配中的灵活性和强大功能。

通过深入了解记录类型的内部机制,我们不仅知其然,还知其所以然,这将帮助我们更好地运用记录类型,编写出更加高效、优雅的代码 。

六、第五步:实际应用,记录类型大展身手

前面我们已经深入了解了 C# 9.0 记录类型的各种特性和内部机制,那么在实际的项目开发中,记录类型又能在哪些场景中发挥其强大的作用呢?下面我们将通过几个常见的应用场景来展示记录类型的实际优势。

数据传输对象(DTO)

在现代软件开发中,尤其是在分布式系统和 Web 应用程序中,数据传输对象(DTO)是一种常用的设计模式 。DTO 主要用于在不同的层(如表示层、业务逻辑层和数据访问层)之间传输数据,它通常只包含数据属性,不包含业务逻辑。

在传统的开发中,我们通常使用类来定义 DTO,例如:

public class UserDTO
{public string Username { get; set; }public string Email { get; set; }public int Age { get; set; }public UserDTO(string username, string email, int age){Username = username;Email = email;Age = age;}
}

使用类来定义 DTO 需要编写较多的样板代码,包括属性定义、构造函数等。而且,在比较两个UserDTO对象是否相等时,还需要手动重写Equals和GetHashCode方法。

而使用 C# 9.0 的记录类型来定义UserDTO,代码会变得非常简洁:

public record UserDTO(string Username, string Email, int Age);

仅仅这一行代码,就完成了属性定义、构造函数生成以及相等性比较方法的实现。在数据传输过程中,我们可以方便地创建UserDTO的实例并进行数据传递。例如,在ASP.NET Core Web API 中,我们可以将UserDTO作为控制器方法的返回类型或参数类型:

[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{[HttpGet("{id}")]public UserDTO GetUser(int id){// 从数据库或其他数据源获取用户数据var user = new UserDTO("JohnDoe", "johndoe@example.com", 30);return user;}[HttpPost]public IActionResult CreateUser(UserDTO userDTO){// 处理创建用户的逻辑return Ok();}
}

由于记录类型的属性默认是不可变的,这符合 DTO 在数据传输过程中只传递数据而不修改数据的特性,保证了数据的一致性和安全性 。同时,记录类型的自动相等性比较功能在处理 DTO 的比较和去重等操作时也非常方便,减少了开发人员的工作量。

领域模型

在领域驱动设计(DDD)中,领域模型是对业务领域的抽象表示,它包含了业务规则和业务逻辑 。在领域模型中,有一些对象通常被称为值对象,它们主要用于表示一些具有特定业务含义的数据,例如货币金额、日期范围等。值对象的特点是它们的相等性是基于其属性值的,而不是基于对象的引用。

在传统的 C# 开发中,我们通常使用类来定义值对象,并手动实现基于属性值的相等性比较方法。例如,定义一个表示货币金额的值对象Money:

public class Money
{public decimal Amount { get; }public string Currency { get; }public Money(decimal amount, string currency){Amount = amount;Currency = currency;}public override bool Equals(object obj){if (obj == null || GetType()!= obj.GetType()){return false;}Money other = (Money)obj;return Amount == other.Amount && Currency == other.Currency;}public override int GetHashCode(){return HashCode.Combine(Amount, Currency);}
}

上述代码中,我们不仅要定义属性和构造函数,还要手动重写Equals和GetHashCode方法来实现基于属性值的相等性比较。

而使用 C# 9.0 的记录类型来定义Money值对象,代码会变得更加简洁:

public record Money(decimal Amount, string Currency);

记录类型会自动为Money生成基于属性值的相等性比较方法,这使得在领域模型中处理值对象时更加方便和高效。例如,在进行业务逻辑处理时,我们可以直接比较两个Money对象是否相等:

var money1 = new Money(100.0m, "USD");
var money2 = new Money(100.0m, "USD");
if (money1 == money2)
{// 处理相等的业务逻辑
}

此外,记录类型的不可变性也符合值对象的特性,即值对象一旦创建,其属性值就不应该被修改。这有助于确保领域模型的一致性和正确性。

配置对象

在应用程序中,配置对象用于存储应用程序的配置信息,例如数据库连接字符串、日志级别等。配置信息在应用程序运行期间通常是固定不变的,因此使用不可变的数据结构来表示配置对象是一个很好的选择。

在传统的 C# 开发中,我们可以使用类来定义配置对象,并通过将属性设置为只读来实现不可变性。例如:

public class AppConfig
{public string ConnectionString { get; }public string LogLevel { get; }public AppConfig(string connectionString, string logLevel){ConnectionString = connectionString;LogLevel = logLevel;}
}

使用类来定义配置对象需要手动编写构造函数和属性定义,并且在比较两个配置对象是否相等时也需要手动实现相等性比较方法。

而使用 C# 9.0 的记录类型来定义AppConfig配置对象,代码会更加简洁:

public record AppConfig(string ConnectionString, string LogLevel);

记录类型的自动相等性比较和不可变性特性使得配置对象的定义和使用更加方便和安全。例如,在读取配置文件并创建配置对象时,我们可以这样使用:

var config = new AppConfig("Server=localhost;Database=MyDB;User=sa;Password=password", "Info");

在应用程序中,我们可以方便地比较不同的配置对象是否相等,以确保配置的一致性。例如:

var config1 = new AppConfig("Server=localhost;Database=MyDB;User=sa;Password=password", "Info");
var config2 = new AppConfig("Server=localhost;Database=MyDB;User=sa;Password=password", "Info");
if (config1 == config2)
{// 配置相同,继续执行应用程序
}

通过以上几个实际应用场景的展示,我们可以看到 C# 9.0 的记录类型在简化数据结构定义、提高代码可读性和开发效率方面具有显著的优势。无论是在数据传输对象、领域模型还是配置对象等场景中,记录类型都能够发挥其强大的功能,帮助我们编写出更加优雅、高效的代码 。

七、总结:记录类型带来的变革与展望

C# 9.0 的记录类型无疑为我们的编程世界带来了一场意义深远的变革,它以简洁高效的特性,重塑了我们定义和操作数据结构的方式。

从定义的简洁性来看,记录类型告别了传统类定义中冗长的构造函数和繁琐的样板代码,只需一行代码就能轻松定义一个包含多个属性的数据结构,大大减少了开发过程中的重复劳动,让代码更加简洁易读。在对象克隆方面,with关键字的引入堪称神来之笔,它使得对象的克隆和属性修改变得轻而易举,不仅简化了代码逻辑,还遵循了不可变对象的设计原则,提高了代码的安全性和可维护性。而自动实现的基于属性值的相等性比较功能,更是解决了传统开发中手动重写Equals和GetHashCode方法的烦恼,让相等性判断变得直观而准确,在集合操作、数据去重等场景中发挥了巨大的作用 。

在实际应用中,记录类型在数据传输对象(DTO)、领域模型、配置对象等多个场景中都展现出了强大的优势,它能够帮助我们编写出更加优雅、高效的代码,提升项目的整体质量和开发效率。

对于广大开发者而言,C# 9.0 记录类型是一个不可多得的强大工具。我强烈建议大家在今后的项目开发中积极尝试使用记录类型,尤其是在处理那些简单的数据承载对象和需要强调不可变性的数据结构时,记录类型将为你带来意想不到的便利和效率提升。

展望未来,随着 C# 语言的不断发展和演进,我们有理由相信记录类型也将不断完善和增强。未来的版本中,记录类型可能会在性能优化、与其他语言特性的融合等方面取得更大的突破,为开发者提供更加丰富和强大的功能。让我们拭目以待,继续探索 C# 记录类型的无限可能,在编程的道路上不断前行 。

相关文章:

C# 9.0记录类型:解锁开发效率的魔法密码

一、引言&#xff1a;记录类型的神奇登场 在 C# 的编程世界中&#xff0c;数据结构就像是构建软件大厦的基石&#xff0c;其重要性不言而喻。然而&#xff0c;传统的数据结构定义方式&#xff0c;尤其是在处理简单的数据承载对象时&#xff0c;常常显得繁琐复杂。例如&#xf…...

Python 函数魔法书:基础、范例、避坑、测验与项目实战

Python 函数魔法书&#xff1a;基础、范例、避坑、测验与项目实战 内容简介 本系列文章是为 Python3 学习者精心设计的一套全面、实用的学习指南&#xff0c;旨在帮助读者从基础入门到项目实战&#xff0c;全面提升编程能力。文章结构由 5 个版块组成&#xff0c;内容层层递进…...

Unbutu虚拟机+eclipse+CDT编译调试环境搭建

问题1: 安装CDT&#xff0c;直接Help->eclipse Market space-> 搜cdt , install&#xff0c;等待重启即可. 问题2&#xff1a;C变量不识别vector ’could not be resolved 这是库的头文件没加好&#xff0c;右键Properties->C Build->Enviroment&#xff0c;增加…...

项目部署(springboot项目)

1、安装Nginx&#xff0c;并开启 2、前端项目打包&#xff1a;npm run build:prod--->dist 3、后端项目打包&#xff1a;install--->xxx.jar 4、开放需要的端口号&#xff1a;比如我的后端项目端口号为8282&#xff0c;则需要防火墙和服务器同时开发8282端口 5、将di…...

Spring MVC拦截器

文章目录 1. 拦截器(interceptor)的作用2. 拦截器和过滤器区别3. 拦截器是快速入门 1. 拦截器(interceptor)的作用 Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter&#xff0c;用于对处理器进行预处理和后处理。 将拦截器按一定的顺序联结成一条链&#xff0c;这条…...

Nginx 路由匹配(Nginx Route Matching)

从小白到高手&#xff1a;深入Nginx 路由匹配 在现代互联网应用中&#xff0c;Nginx 作为一款高性能的 Web 服务器&#xff0c;因其灵活性和高效性而广泛应用于各类网站和服务。Nginx 的路由匹配规则是其核心功能之一&#xff0c;负责决定如何处理传入的请求。通过这些规则&am…...

基于RIP的MGRE实验

实验拓扑 实验要求 按照图示配置IP地址配置静态路由协议&#xff0c;搞通公网配置MGRE VPNNHRP的配置配置RIP路由协议来传递两端私网路由测试全网通 实验配置 1、配置IP地址 [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 15.0.0.1 24 [R1]int LoopBack 0 [R1-LoopBack0]i…...

Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)

目录 前言1. 基本知识2. Demo3. 实战代码 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 基本的Java知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&am…...

【Java基础-41.5】深入解析Java异常链:构建清晰的错误追踪体系

在Java编程中&#xff0c;异常处理是保证程序健壮性和可维护性的重要部分。然而&#xff0c;在实际开发中&#xff0c;异常往往不是孤立发生的&#xff0c;而是由一系列相关的异常引发的。为了更好地理解和处理这种复杂的异常场景&#xff0c;Java引入了 异常链&#xff08;Exc…...

STM32使用VScode开发

文章目录 Makefile形式创建项目新建stm项目下载stm32cubemx新建项目IED makefile保存到本地arm gcc是编译的工具链G++配置编译Cmake +vscode +MSYS2方式bilibiliMSYS2 统一环境配置mingw32-make -> makewindows环境变量Cmake CmakeListnijia 编译输出elfCMAKE_GENERATOR查询…...

特权模式docker逃逸

目录 1.环境 2.上线哥斯拉 3.特权模式逃逸 1.判断是否为docker环境 2.判断是否为特权模式 3.挂载宿主机磁盘到docker 4.计划任务反弹shell 1.环境 ubuntu部署一个存在CVE-2017-12615的docker: (ip:192.168.117.147) kali(ip:192.168.117.128) 哥斯拉 2.上线哥斯拉…...

装出字符串中国第一个匹配项的下标

hello 大家好&#xff01;今天开写一个新章节&#xff0c;每一天一道算法题。让我们一起来学习算法思维吧&#xff01; function strStr(haystack, needle) {return haystack.indexOf(needle); }// 测试示例 const haystack "sadbutsad"; const needle "sad&q…...

从腾讯云数据仓库TCHouse安全地转移数据到AWS Redshift

实现从AWS Direct Connect连接到腾讯云数据仓库TCHouse-P、TCHouse-C或TCHouse-D&#xff0c;然后使用AWS Glue读取数据并在AWS Redshift中创建对应表并复制数据&#xff0c;需要按照以下步骤进行操作&#xff1a; 网络连接设置 AWS Direct Connect配置&#xff1a; 在AWS管理…...

DataComp:探索下一代多模态数据集

目录 一、TL;DR 二、方法 2.1 为什么要单独研究数据质量&#xff1f; 2.2 数据质量的研究范式 三、其他的工作&#xff08;related work&#xff09; 3.1 传统的做法 3.2 数据剪枝和去重&#xff08;paper直接翻译&#xff09; 四、DataComp的benchmark 4.1 竞赛条件限…...

【linux】Linux 常见目录特性、权限和功能

目录特性默认权限主要功能/用途/根目录&#xff0c;所有目录的起点755文件系统的顶层目录&#xff0c;包含所有其他子目录和文件/bin基础二进制命令目录&#xff08;系统启动和修复必需的命令&#xff09;755存放所有用户可用的基本命令&#xff08;如 ls, cp, bash 等&#xf…...

基于SpringBoot电脑组装系统平台系统功能实现六

一、前言介绍&#xff1a; 1.1 项目摘要 随着科技的进步&#xff0c;计算机硬件技术日新月异&#xff0c;包括处理器&#xff08;CPU&#xff09;、主板、内存、显卡等关键部件的性能不断提升&#xff0c;为电脑组装提供了更多的选择和可能性。不同的硬件组合可以构建出不同类…...

Direct2D 极速教程(1) —— 画图形

极速导航 Direct2D 简介创建新项目&#xff1a;001-DrawGraphics弄一个白窗口在窗口上画图 Direct2D 简介 大家在学 WINAPI 的时候的时候有没有想过&#xff0c;怎么在一副窗口上画图呢&#xff1f;大家知道 Windows 系统是 GUI 图形用户界面 系统&#xff0c;以 Graphics 图形…...

DF 开发1

https://www.bilibili.com/video/BV1RFChYxEhJ/ 多个 workspace 图片上传 S3 上传大量文档 https://www.bilibili.com/video/BV1ySsEeUE6i 解决方案 返回 metadata https://www.bilibili.com/video/BV1t3e5eaENo 给出内容引用出处 模型负载均衡 可以以 ollama 在不同端口起服…...

[Computer Vision]实验二:图像特征点提取

目录 一、实验内容 二、实验过程及结果 2.1 Harris角点检测 2.2 SIFT算法 三、实验小结 一、实验内容 采用Harris与SIFT分别提取特征点及对应的描述子&#xff0c;对比两者的区别&#xff08;特征点数量、分布、描述子维度、图像变化对二者的影响等&#xff09;利用特征匹…...

在做题中学习(82):最小覆盖子串

解法&#xff1a;同向双指针——>滑动窗口 思路&#xff1a;题目要求找到s里包含t所有字符的最小子串&#xff0c;这就需要记录在s中每次查找并扩大范围时所包含进去的字符种类是否和t的相同&#xff0c;并且&#xff1a;题目提示t中会有重复字符&#xff0c;因此不能简单认…...

< OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)

目标&#xff1a; 使用 日本阿里云的 VPM 传输文件。 暂时方案&#xff1a; 使用 主机JPN 下载 https://huggingface.co/ 上模型从 JPN 放到 度狗上在家里从狗度下载 为了减少编程&#xff0c;尽量使用现在软件 &#xff0c;就找到 GitHub - qjfoidnh/BaiduPCS-Go: iikira…...

redis缓存和springboot缓存包冲突怎么办

如果Redis缓存与Spring Boot缓存包发生冲突&#xff0c;可以采取以下几种解决方案&#xff1a; 排除Spring Boot缓存包&#xff1a;在pom.xml文件中排除Spring Boot的缓存依赖&#xff0c;以避免与Redis缓存冲突。例如&#xff1a; <dependency><groupId>org.spr…...

云计算技术深度解析与代码使用案例

云计算技术深度解析与代码使用案例 引言 随着信息技术的飞速发展,云计算作为一种革命性的技术,正在逐步改变我们的生活和工作方式。云计算不仅提供了前所未有的计算能力和存储资源,还以其灵活性和可扩展性,成为现代企业数字化转型的重要支撑。本文将深入探讨云计算的核心…...

【教学类-89-01】20250127新年篇01—— 蛇年红包(WORD模版)

祈愿在2025蛇年里&#xff0c; 伟大的祖国风调雨顺、国泰民安、每个人齐心协力&#xff0c;共同经历这百年未有之大变局时代&#xff08;国际政治、AI技术……&#xff09; 祝福亲友同事孩子们平安健康&#xff08;安全、安全、安全&#xff09;、巳巳如意&#xff01; 背景需…...

React Router v6配置路由守卫

首先准备好以下页面 登录页&#xff1a;用户可以在此页面登录。 受保护页&#xff1a;只有登录的用户可以访问&#xff0c;否则会重定向到登录页。 公共页面&#xff1a;不需要鉴权&#xff0c;任何人都可以访问。 1. 安装依赖 首先&#xff0c;我们需要安装 react-router-do…...

双层Git管理项目,github托管显示正常

双层Git管理项目&#xff0c;github托管显示正常 背景 在写React项目时&#xff0c;使用Next.js,该项目默认由git托管。但是我有在项目代码外层记笔记的习惯&#xff0c;我就在外层使用了git托管。 目录如下 code 层内也有.git 文件&#xff0c;对其托管。 我没太在意&…...

Linux--权限

Linux系统的权限管理是保障系统安全的重要机制&#xff0c;以下详细讲解权限相关概念及操作指令&#xff1a; 一、基础权限机制 1. 权限的三元组&#xff0c;读&#xff08;r&#xff09;、写&#xff08;w&#xff09;、执行&#xff08;x&#xff09; 每个文件或目录有三组…...

第25章 项目启航前的密谈

在那弥漫着严谨与专注气息的会议室里&#xff0c;苏睿所长端坐在会议桌前&#xff0c;宛如一座沉稳的山峰&#xff0c;散发着一种让人安心的力量。他的神情认真而庄重&#xff0c;目光中透着几分感慨&#xff0c;仿佛在时光的长河中回溯着项目的点点滴滴。微微侧身看向东方艾艾…...

ModernBERT 为我们带来了哪些启示?

当谷歌在 2018 年推出 BERT 模型时&#xff0c;恐怕没有料到这个 3.4 亿参数的模型会成为自然语言处理领域的奠基之作。 六年后的今天&#xff0c;面对动辄千亿参数的大语言模型浪潮&#xff0c;Answer.AI、LightOn与 HuggingFace 联手打造的 ModernBERT 却选择了一条返璞归真的…...

【MySQL】--- 复合查询 内外连接

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; MySQL &#x1f3e0; 基本查询回顾 假设有以下表结构&#xff1a; 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为…...

Android Studio打包APK

1.导出APK安装包 如果是首次打包&#xff0c;Create new 单击蓝色对话框右边文件夹&#x1f4c2;图标 &#xff0c;选择密钥保存路径&#xff0c;然后在下方File name对话框中填写您想要名称&#xff0c;再点击OK回到密钥创建对话框。 在此对话框中填写密码&#xff08;Passwo…...

RKNN_C++版本-YOLOV5

1.背景 为了实现低延时&#xff0c;所以开始看看C版本的rknn的使用&#xff0c;确实有不足的地方&#xff0c;请指正&#xff08;代码借鉴了rk官方的仓库文件&#xff09;。 2.基本的操作流程 1.读取模型初始化 // 设置基本信息 // 在postprocess.h文件中定义&#xff0c;详见…...

Git常用命令集合

见过不少人、经过不少事、也吃过不少苦&#xff0c;感悟世事无常、人心多变&#xff0c;靠着回忆将往事串珠成链&#xff0c;聊聊感情、谈谈发展&#xff0c;我慢慢写、你一点一点看...... git init <directory》初始化本地仓库 git add <file> 添加文件到暂存区 git …...

【deepseek】deepseek-r1本地部署-第一步:下载LM Studio

要下载LM Studio&#xff0c;可以按照以下步骤进行&#xff1a; 一、访问LM Studio官方网站 打开必应&#xff08;注意&#xff01;百度无法打开官网&#xff09;&#xff0c;输入LM Studio的官方网址&#xff1a;LM Studio - Discover, download, and run local LLMs。进入L…...

【数据结构】_链表经典算法OJ:合并两个有序数组

目录 1. 题目描述及链接 2. 解题思路 3. 程序 3.1 第一版 3.2 第二版 1. 题目描述及链接 题目链接&#xff1a;21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。 新链表是通过拼接给…...

mybatis(78/134)

前天学了很多&#xff0c;关于java的反射机制&#xff0c;其实跳过了new对象&#xff0c;然后底层生成了字节码&#xff0c;创建了对应的编码。手搓了一遍源码&#xff0c;还是比较复杂的。 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE …...

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

文章目录 指令格式&#xff08;重点&#xff09;1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…...

Mybatis配置文件详解

MyBatis通过XML或注解的方式将Java对象与数据库中的记录进行映射&#xff0c;极大地简化了数据访问层的开发。而在MyBatis的核心组成部分中&#xff0c;配置文件扮演着举足轻重的角色。它不仅定义了MyBatis的运行环境&#xff0c;还配置了数据源、事务管理、映射器等关键元素&a…...

一组开源、免费、Metro风格的 WPF UI 控件库

前言 今天大姚给大家分享一个开源、免费、Metro风格的 WPF UI 控件库&#xff1a;MahApps.Metro。 项目介绍 MahApps.Metro 是一个开源、免费、Metro风格的 WPF UI 控件库&#xff0c;提供了现代化、平滑和美观的控件和样式&#xff0c;帮助开发人员轻松创建具有现代感的 Win…...

.NET MAUI 入门学习指南

引言 在当今移动应用和跨平台开发的热潮中,.NET MAUI(Multi - platform App UI)应运而生,为开发者提供了一种高效、统一的方式来构建跨多个平台(如 iOS、Android、Windows 等)的原生应用。它整合了 Xamarin.Forms 的优点,并在此基础上进行了诸多改进和创新,使得开发者…...

【超详细】ELK实现日志采集(日志文件、springboot服务项目)进行实时日志采集上报

本文章介绍&#xff0c;Logstash进行自动采集服务器日志文件&#xff0c;并手把手教你如何在springboot项目中配置logstash进行日志自动上报与日志自定义格式输出给logstash。kibana如何进行配置索引模式&#xff0c;可以在kibana中看到采集到的日志 日志流程 logfile-> l…...

本地大模型编程实战(04)给文本自动打标签

文章目录 准备实例化本地大模型情感分析更精细的控制总结代码 使用本地大模型可以根据需要给文本打标签&#xff0c;本文介绍了如何基于 langchain 和本地部署的大模型给文本打标签。 本文使用 llama3.1 作为本地大模型&#xff0c;它的性能比非开源大模型要查一下&#xff0c;…...

JavaScript反爬技术解析与应对

JavaScript 反爬技术解析与应对 前言 在当今 Web 爬虫与数据抓取的生态环境中&#xff0c;网站运营方日益关注数据安全与隐私保护&#xff0c;因此逐步采用多种反爬技术来限制非授权访问。本文从 JavaScript 角度出发&#xff0c;深入剖析主流反爬策略的技术原理&#xff0c;…...

【C++动态规划 状态压缩】2741. 特别的排列|2020

本文涉及知识点 C动态规划 状态压缩 LeetCode2741. 特别的排列 给你一个下标从 0 开始的整数数组 nums &#xff0c;它包含 n 个 互不相同 的正整数。如果 nums 的一个排列满足以下条件&#xff0c;我们称它是一个特别的排列&#xff1a; 对于 0 < i < n - 1 的下标 i…...

省级数字经济发展水平数据(2011-2022年)-社科数据

省级数字经济发展水平数据&#xff08;2011-2022年&#xff09;-社科数据https://download.csdn.net/download/paofuluolijiang/90028602 https://download.csdn.net/download/paofuluolijiang/90028602 数字经济是指以数据资源为关键要素、以现代信息网络为主要载体、以信息…...

【问题解决】el-upload数据上传成功后不显示成功icon

el-upload数据上传成功后不显示成功icon 原因 由于后端返回数据与要求形式不符&#xff0c;使用el-upload默认方法调用onSuccess钩子失败&#xff0c;上传文件的状态并未发生改变&#xff0c;因此数据上传成功后并未显示成功的icon标志。 解决方法 点击按钮&#xff0c;调用…...

新站如何快速获得搜索引擎收录?

本文来自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/8.html 新站想要快速获得搜索引擎收录&#xff0c;需要采取一系列有针对性的策略。以下是一些具体的建议&#xff1a; 一、网站内容优化 高质量原创内容&#xff1a; 确保网站内容原创、…...

判断子序列

hello 大家好&#xff01;今天开写一个新章节&#xff0c;每一天一道算法题。让我们一起来学习算法思维吧&#xff01; function isSubsequence(s, t) {// 初始化两个指针&#xff0c;分别指向字符串 s 和 t 的起始位置let i 0; let j 0; // 当两个指针都未超出对应字符串的长…...

【Leetcode 热题 100】416. 分割等和子集

问题背景 给你一个 只包含正整数 的 非空 数组 n u m s nums nums。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 数据约束 1 ≤ n u m s . l e n g t h ≤ 200 1 \le nums.length \le 200 1≤nums.length≤200 1 ≤ n u m s [ i ] ≤ …...

Kotlin开发(六):Kotlin 数据类,密封类与枚举类

引言 想象一下&#xff0c;你是个 Kotlin 开发者&#xff0c;敲着代码忽然发现业务代码中需要一堆冗长的 POJO 类来传递数据。烦得很&#xff1f;别急&#xff0c;Kotlin 贴心的 数据类 能帮你自动生成 equals、hashCode&#xff0c;直接省时省力&#xff01;再想想需要多种状…...