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

解锁C#自定义属性:从0到1的深度实践指南

一、开篇:自定义属性的奇妙世界

在 C# 的编程宇宙中,自定义属性是一个强大且迷人的存在,它就像是代码世界里的魔法标记,为我们的程序赋予了丰富的元数据,让代码变得更加智能和富有表现力。

想象一下,你正在开发一个大型的企业级应用,其中涉及到复杂的数据存储和业务逻辑。在数据库表和实体类之间的映射过程中,你需要一种方式来清晰地标记每个实体类属性对应的数据库字段名、字段类型以及是否为主键等信息。又或者,在进行权限管理时,你希望能够轻松地标记哪些方法需要特定的权限才能访问。再比如,在实现数据验证时,能够直接在属性上标记验证规则。这时,C# 的自定义属性就如同救星一般降临。

自定义属性允许我们为程序集、类型、成员(类、方法、属性、事件等)添加额外的描述性或指示性信息。这些信息就像是代码的 “隐形注释”,不仅在编译时能够被编译器处理,在运行时还能通过反射机制被灵活访问 ,进而实现各种强大的功能,如配置管理、序列化、代码分析、代码生成等。接下来,就让我们一同深入探索 C# 自定义属性的精彩世界,从定义到应用,再到反射访问,一步步揭开它神秘的面纱。

二、理论基石:自定义属性是什么

(一)概念与定义

C# 中的自定义属性,从本质上来说,是一种强大的元数据机制 。它允许开发者为程序集、类型(类、结构体、枚举等)、成员(方法、属性、字段、事件等)添加额外的描述性或指示性信息。这些信息并不直接影响程序的运行逻辑,但却为程序提供了丰富的上下文和元数据,就像是给代码贴上了各种 “标签”,方便在不同的场景下对代码进行处理和理解。

所有的自定义属性都必须继承自System.Attribute基类,这是自定义属性的根基。在继承这个基类之后,开发者可以根据具体的需求,在自定义属性类中定义构造函数、字段、属性以及方法,以此来存储和操作与目标程序元素相关的元数据。例如,我们可以创建一个自定义属性类AuthorAttribute,用于标记某个类或方法的作者信息:

using System;// 自定义属性类,继承自System.Attribute
public class AuthorAttribute : Attribute
{// 作者姓名public string Name { get; set; }// 作者邮箱public string Email { get; set; }// 构造函数,接收作者姓名public AuthorAttribute(string name){Name = name;}
}

在上述代码中,AuthorAttribute类继承自System.Attribute,它包含了两个属性Name和Email,分别用于存储作者的姓名和邮箱。构造函数接收一个name参数,用于初始化作者姓名。这样,我们就创建了一个简单的自定义属性类,它可以为目标程序元素添加作者相关的元数据。

(二)作用与意义

自定义属性在 C# 编程中有着广泛而重要的作用,它就像是代码的 “隐形助手”,默默地为代码的可维护性、可扩展性以及各种高级功能的实现提供着强大的支持。

在配置管理方面,自定义属性可以用于标记特定的配置项或配置节。例如,在一个 Web 应用程序中,我们可以创建一个ConfigurationSectionAttribute自定义属性,用于标记哪些类或属性是与特定的配置节相关联的。这样,在读取和解析配置文件时,就可以通过反射获取这些标记了自定义属性的元素,从而方便地进行配置管理。例如:

// 自定义配置节属性
[AttributeUsage(AttributeTargets.Class)]
public class ConfigurationSectionAttribute : Attribute
{public string SectionName { get; set; }public ConfigurationSectionAttribute(string sectionName){SectionName = sectionName;}
}// 标记配置节类
[ConfigurationSection("DatabaseSettings")]
public class DatabaseConfig
{public string ConnectionString { get; set; }
}

在代码分析领域,自定义属性可以帮助分析工具更好地理解代码的结构和意图。比如,我们可以创建一个DependsOnAttribute自定义属性,用于标记某个类或方法依赖于哪些其他的组件或服务。代码分析工具在扫描代码时,通过识别这些自定义属性,就能够生成更准确的依赖关系图,帮助开发者更好地理解代码的架构,发现潜在的问题。示例如下:

// 自定义依赖属性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DependsOnAttribute : Attribute
{public string Dependency { get; set; }public DependsOnAttribute(string dependency){Dependency = dependency;}
}// 标记依赖的类
[DependsOn("UserService")]
public class OrderService
{// 业务逻辑
}

对于代码生成,自定义属性同样发挥着关键作用。在一些代码生成工具中,通过在模板代码中使用自定义属性来标记需要替换或生成代码的位置。例如,我们可以创建一个GenerateCodeAttribute自定义属性,用于指示代码生成器在特定的位置生成特定的代码。这样,在开发过程中,就可以根据不同的需求,通过修改自定义属性的值或逻辑,快速生成符合要求的代码,提高开发效率。

// 自定义代码生成属性
[AttributeUsage(AttributeTargets.Method)]
public class GenerateCodeAttribute : Attribute
{public string Template { get; set; }public GenerateCodeAttribute(string template){Template = template;}
}// 标记需要生成代码的方法
[GenerateCode("SomeCodeTemplate")]
public void GenerateSomeCode()
{// 这里的代码将被生成的代码替换
}

此外,自定义属性对于代码的可维护性和扩展性有着显著的提升作用。通过使用自定义属性,我们可以将一些与业务逻辑无关的配置信息、标记信息等从代码中分离出来,使得代码更加简洁、清晰。同时,当需求发生变化时,我们只需要修改自定义属性的定义或使用方式,而不需要对大量的业务逻辑代码进行修改,从而大大提高了代码的可维护性和扩展性。例如,在一个权限管理系统中,我们可以通过自定义属性来标记哪些方法需要特定的权限才能访问。当权限规则发生变化时,只需要修改自定义属性的逻辑,而不需要在每个方法中修改权限验证代码,使得代码的维护和扩展变得更加容易。

三、实战起步:自定义属性定义

(一)基础语法与规则

在 C# 中定义自定义属性,最基础的规则就是属性类必须继承自System.Attribute基类 ,这是自定义属性的根本所在。例如:

public class MyCustomAttribute : Attribute
{// 自定义属性的内容
}

同时,为了更好地控制自定义属性的使用方式,我们通常会使用AttributeUsageAttribute特性来对自定义属性进行修饰。AttributeUsageAttribute类包含三个重要的成员:AttributeTargets、Inherited和AllowMultiple 。

AttributeTargets用于指定自定义属性可以应用到哪些程序元素上,比如AttributeTargets.Class表示该属性只能应用于类,AttributeTargets.Method表示只能应用于方法,AttributeTargets.Property表示只能应用于属性等。我们还可以通过 “|” 运算符来组合多个目标,如AttributeTargets.Class | AttributeTargets.Method表示该属性既可以应用于类,也可以应用于方法。例如:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyCustomAttribute : Attribute
{// 自定义属性的内容
}

Inherited属性用于指示该属性是否可由从应用了该属性的类派生的类继承。它采用布尔值,true为默认值,表示可继承;false则表示不可继承。比如:

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class NonInheritableAttribute : Attribute
{// 自定义属性的内容
}

AllowMultiple属性用于指示在同一个程序元素上是否可以存在该属性的多个实例。如果设置为true,则允许存在多个实例;设置为false(默认值),则只允许存在一个实例。例如:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MultipleAllowedAttribute : Attribute
{// 自定义属性的内容
}

按照约定,属性类的名称通常以 “Attribute” 结尾,这样可以提高代码的可读性,虽然这不是强制要求,但建议遵循这一约定。在使用属性时,可以省略 “Attribute” 后缀,例如定义了MyCustomAttribute类,在使用时可以写成[MyCustom],编译器会自动识别。

(二)属性类结构剖析

以一个简单的自定义属性类AuthorAttribute为例,来深入剖析属性类的结构:

using System;// 自定义属性类,继承自System.Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorAttribute : Attribute
{// 作者姓名private string _name;// 作者邮箱private string _email;// 构造函数,接收作者姓名public AuthorAttribute(string name){_name = name;}// 公开属性,用于获取作者姓名public string Name{get { return _name; }}// 公开属性,用于获取和设置作者邮箱public string Email{get { return _email; }set { _email = value; }}
}

在这个属性类中,构造函数起着关键的初始化作用。public AuthorAttribute(string name)构造函数接收一个name参数,用于初始化_name字段,这个字段存储着作者的姓名。通过构造函数,我们可以在应用属性时为属性类提供必要的初始数据。

字段_name和_email用于存储属性的核心数据,它们是属性类的内部状态的重要组成部分。在这个例子中,_name存储作者姓名,_email存储作者邮箱。

属性Name和Email则为外部提供了访问这些内部数据的接口。Name属性是只读的,通过get访问器返回_name字段的值,确保了作者姓名在外部只能被读取,不能被随意修改。而Email属性既有get访问器用于读取邮箱,也有set访问器用于设置邮箱,使得外部代码可以在需要时修改作者邮箱。这种字段和属性的配合,既保证了数据的安全性和封装性,又提供了灵活的访问方式。

(三)实战演练:创建自定义属性类

接下来,我们以定义一个标记类用途的属性ClassUsageAttribute为例,来展示完整的代码实现过程。假设我们希望通过这个属性来标记某个类是用于数据访问层、业务逻辑层还是表示层。

首先,创建一个枚举类型来表示类的用途:

public enum ClassLayer
{DataAccessLayer,BusinessLogicLayer,PresentationLayer
}

然后,定义ClassUsageAttribute属性类:

using System;// 自定义属性类,继承自System.Attribute
[AttributeUsage(AttributeTargets.Class)]
public class ClassUsageAttribute : Attribute
{// 类的用途private ClassLayer _layer;// 构造函数,接收类的用途public ClassUsageAttribute(ClassLayer layer){_layer = layer;}// 公开属性,用于获取类的用途public ClassLayer Layer{get { return _layer; }}
}

在上述代码中,ClassUsageAttribute类继承自System.Attribute,并使用AttributeUsageAttribute特性将其应用范围限定在类上。构造函数public ClassUsageAttribute(ClassLayer layer)接收一个ClassLayer类型的参数layer,用于初始化_layer字段,该字段存储了类的用途信息。公开属性Layer通过get访问器返回_layer字段的值,为外部提供了获取类用途的接口。

现在,我们就可以在类上应用这个自定义属性了,例如:

// 标记为数据访问层类
[ClassUsage(ClassLayer.DataAccessLayer)]
public class UserRepository
{// 数据访问层的代码实现
}// 标记为业务逻辑层类
[ClassUsage(ClassLayer.BusinessLogicLayer)]
public class UserService
{// 业务逻辑层的代码实现
}// 标记为表示层类
[ClassUsage(ClassLayer.PresentationLayer)]
public class UserController
{// 表示层的代码实现
}

通过这样的方式,我们可以清晰地标记每个类所属的层次,为代码的结构和理解提供了便利,也为后续可能的代码分析、模块划分等操作奠定了基础。

四、应用进阶:自定义属性使用

(一)属性应用场景

在实体类中,自定义属性可用于标记数据库表字段的相关信息。例如,在一个数据访问层的开发中,我们可以创建一个TableColumnAttribute自定义属性,用于标记实体类的属性与数据库表列的映射关系,包括列名、数据类型、是否可为空等。这样,在进行数据持久化操作时,通过反射获取这些属性信息,就可以方便地生成 SQL 语句,实现数据的准确存储和读取。

[TableColumn("CustomerId", "int", false)]
public int Id { get; set; }

在方法上,自定义属性可以用于实现权限控制。比如,创建一个RequirePermissionAttribute自定义属性,用于标记某个方法需要特定的权限才能访问。在应用程序的业务逻辑层,当调用这些方法时,通过反射检查方法上的RequirePermissionAttribute属性,获取所需的权限信息,然后与当前用户的权限进行比对,从而实现对方法访问的权限控制。

[RequirePermission("Admin")]
public void DeleteUser(int userId)
{// 删除用户的业务逻辑
}

对于程序集,自定义属性可以用于标记版本信息、作者信息、版权声明等。例如,在项目的 AssemblyInfo.cs 文件中,我们可以使用AssemblyVersionAttribute、AssemblyCopyrightAttribute等预定义属性来标记程序集的版本号和版权声明,也可以创建自定义属性来添加更多的元数据,如项目的构建时间、构建环境等信息。

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyCopyright("Copyright © 2024 YourCompany")]

在 Web API 开发中,自定义属性可以用于标记路由信息、参数验证规则等。比如,使用RouteAttribute来标记控制器的路由地址,还可以创建自定义的参数验证属性,如ValidEmailAttribute用于验证邮箱格式,在方法参数上应用这些属性,在请求到达时,Web API 框架会自动根据这些属性进行路由匹配和参数验证。

[Route("api/[controller]")]
public class UserController : Controller
{[HttpPost]public IActionResult CreateUser([ValidEmail] string email){// 创建用户的逻辑}
}

在测试框架中,自定义属性可以用于标记测试方法、测试类别等。例如,在 NUnit 测试框架中,使用TestAttribute来标记测试方法,还可以创建自定义属性来标记测试方法所属的测试类别,方便对测试用例进行分类管理和执行。

[Test]
[Category("UnitTest")]
public void TestMethod()
{// 测试方法的逻辑
}

(二)在各类元素上的应用方式

在类上应用自定义属性,我们以之前创建的ClassUsageAttribute为例:

// 标记为数据访问层类
[ClassUsage(ClassLayer.DataAccessLayer)]
public class UserRepository
{// 数据访问层的代码实现
}

在方法上应用自定义属性,以AuthorAttribute为例:

[Author(Name = "John Doe", Email = "johndoe@example.com")]
public void SomeMethod()
{// 方法的代码实现
}

在属性上应用自定义属性,比如创建一个RequiredAttribute用于标记属性是否为必填:

public class User
{[Required]public string Name { get; set; }
}

在字段上应用自定义属性,例如创建一个ReadOnlyAttribute用于标记字段是否为只读:

public class SomeClass
{[ReadOnly]private int _readOnlyField;
}

在事件上应用自定义属性,比如创建一个EventDescriptionAttribute用于描述事件的用途:

public class EventPublisher
{[EventDescription("This event is raised when data is updated")]public event EventHandler DataUpdated;
}

(三)多属性应用与注意事项

在同一元素上应用多个属性时,只需按照顺序依次列出即可。例如,在一个方法上同时应用AuthorAttribute和PerformanceMonitorAttribute:

[Author(Name = "Jane Smith", Email = "janesmith@example.com")]
[PerformanceMonitor]
public void ComplexMethod()
{// 复杂方法的代码实现
}

在应用多个属性时,需要注意属性的顺序。某些情况下,属性的顺序可能会影响其执行逻辑或效果。例如,在进行数据验证时,如果有多个验证属性应用在一个属性上,不同的顺序可能导致不同的验证结果。

同时,要确保多个属性之间不会产生冲突。比如,两个属性对同一元素的行为产生了相反的定义,这可能会导致运行时错误或不可预期的结果。在创建和应用属性时,要充分考虑属性之间的兼容性和协同工作能力。

另外,在使用反射获取属性时,需要注意如果同一类型的属性有多个实例,获取时要明确是获取单个实例还是获取所有实例。例如,GetCustomAttribute()方法只会获取第一个匹配的属性实例,而GetCustomAttributes()方法会获取所有匹配的属性实例 ,根据实际需求选择合适的方法进行属性获取。

五、深度探索:反射访问自定义属性

(一)反射原理与机制

在 C# 的编程世界中,反射是一种强大的机制,它允许程序在运行时动态地获取类型信息、创建类型实例、访问和操作对象的成员(如属性、方法、字段等)。这种动态特性为程序的灵活性和扩展性提供了巨大的支持,使得我们能够编写更加通用和智能的代码。

从本质上来说,当我们编写 C# 代码并进行编译时,编译器会将代码转换为中间语言(IL),并在其中包含丰富的元数据。这些元数据描述了代码中的类型、成员、方法签名、属性等信息。反射机制就是基于这些元数据工作的,它在运行时能够读取这些元数据,从而获取到关于类型的各种信息 。

例如,当我们使用typeof关键字获取一个类型的Type对象时,实际上就是在获取该类型的元数据的入口。通过这个Type对象,我们可以进一步获取该类型的所有属性、方法、构造函数等信息。例如:

Type myType = typeof(MyClass);
PropertyInfo[] properties = myType.GetProperties();
MethodInfo[] methods = myType.GetMethods();
ConstructorInfo[] constructors = myType.GetConstructors();

在上述代码中,typeof(MyClass)获取了MyClass类型的Type对象,然后通过该对象的GetProperties、GetMethods和GetConstructors方法,分别获取了MyClass类型的所有属性、方法和构造函数的信息。

反射的工作原理涉及到System.Reflection命名空间下的一系列类和接口。其中,Assembly类用于表示程序集,我们可以通过它加载程序集、获取程序集中定义的类型等。Type类则是反射的核心,它代表了一个类型,提供了大量的方法和属性来获取类型的各种信息 。PropertyInfo类用于描述属性,MethodInfo类用于描述方法,ConstructorInfo类用于描述构造函数,通过这些类,我们可以在运行时动态地访问和操作对象的成员。例如,通过PropertyInfo类的GetValue和SetValue方法,我们可以获取和设置对象属性的值;通过MethodInfo类的Invoke方法,我们可以调用对象的方法。

(二)通过反射获取属性信息

在 C# 中,使用反射获取属性信息主要依赖于System.Reflection命名空间下的Type类和PropertyInfo类 。下面以一个具体的示例来详细说明。

假设有一个Person类,定义如下:

public class Person
{[DisplayName("姓名")]public string Name { get; set; }[DisplayName("年龄")]public int Age { get; set; }
}

其中,DisplayName是一个自定义属性,用于标记属性的显示名称。

要获取Person类的属性信息以及属性上的自定义属性信息,可以使用以下代码:

Type personType = typeof(Person);
PropertyInfo[] properties = personType.GetProperties();foreach (PropertyInfo property in properties)
{Console.WriteLine($"属性名: {property.Name}");// 获取属性上的DisplayName自定义属性DisplayNameAttribute displayNameAttr = property.GetCustomAttribute<DisplayNameAttribute>();if (displayNameAttr!= null){Console.WriteLine($"显示名称: {displayNameAttr.Name}");}// 获取属性的类型Console.WriteLine($"属性类型: {property.PropertyType.Name}");
}

在上述代码中,首先通过typeof(Person)获取Person类的Type对象,然后调用GetProperties方法获取Person类的所有属性信息,这些信息存储在PropertyInfo数组中。

接着,通过foreach循环遍历每个PropertyInfo对象。对于每个属性,首先输出其名称。然后,使用GetCustomAttribute()方法获取属性上的DisplayNameAttribute自定义属性 ,如果获取到该属性,则输出其显示名称。最后,通过PropertyType.Name获取属性的类型名称并输出。

通过这种方式,我们可以在运行时动态地获取类的属性信息以及属性上的自定义属性信息,为实现各种动态功能提供了基础。

(三)反射应用实战:属性信息处理

假设我们正在开发一个数据验证框架,需要根据属性上的自定义属性来对对象的属性值进行验证。以之前的Person类为例,我们可以创建一个RequiredAttribute自定义属性,用于标记属性是否为必填:

public class RequiredAttribute : Attribute
{
}

修改Person类,为Name属性添加RequiredAttribute属性:

public class Person
{[Required][DisplayName("姓名")]public string Name { get; set; }[DisplayName("年龄")]public int Age { get; set; }
}

接下来,编写一个验证方法,使用反射遍历Person对象的属性,并根据属性上的RequiredAttribute属性进行验证:

public static bool ValidateObject(object obj)
{Type type = obj.GetType();PropertyInfo[] properties = type.GetProperties();foreach (PropertyInfo property in properties){// 获取属性上的RequiredAttribute属性RequiredAttribute requiredAttr = property.GetCustomAttribute<RequiredAttribute>();if (requiredAttr!= null){object value = property.GetValue(obj);if (value == null || string.IsNullOrEmpty(value.ToString())){Console.WriteLine($"属性 {property.Name} 是必填项。");return false;}}}return true;
}

在上述代码中,ValidateObject方法接收一个object类型的参数obj,表示要验证的对象。首先通过obj.GetType()获取对象的类型,然后获取该类型的所有属性。

在foreach循环中,对于每个属性,使用GetCustomAttribute()方法获取属性上的RequiredAttribute属性。如果存在该属性,则通过property.GetValue(obj)获取属性的值,并进行非空和非空字符串的验证。如果验证不通过,则输出错误信息并返回false。

如果所有属性都验证通过,则返回true。通过这种方式,我们利用反射和自定义属性实现了一个简单的数据验证功能,展示了反射在实际应用中的强大作用。

六、总结与展望:回顾与未来方向

(一)知识回顾与总结

在本次对 C# 自定义属性的探索中,我们深入了解了其定义、应用和反射访问的关键知识点。自定义属性作为一种强大的元数据机制,继承自System.Attribute基类,通过AttributeUsageAttribute特性来精确控制其使用范围、继承性和多重应用等特性 。在定义属性类时,构造函数、字段和属性的合理设计,为属性类赋予了丰富的功能和灵活的数据存储与访问方式。

在应用层面,自定义属性广泛应用于实体类、方法、程序集等各类元素上,为数据访问、权限控制、配置管理等众多场景提供了有力支持。通过在代码中合理地应用自定义属性,我们能够为程序添加丰富的元数据,使得代码的意图更加清晰,可维护性和可扩展性得到显著提升。

而反射机制则为我们在运行时动态获取和处理自定义属性信息提供了可能。借助System.Reflection命名空间下的Type类和PropertyInfo类等,我们可以轻松地获取类型信息、属性信息以及属性上的自定义属性信息,从而实现各种动态功能,如数据验证、代码生成等。

(二)未来应用拓展与思考

随着技术的不断发展,C# 自定义属性在新兴技术和开发场景中展现出了巨大的应用潜力。在云计算和分布式系统中,自定义属性可以用于标记服务的特性、依赖关系以及部署配置等信息。例如,通过自定义属性标记某个微服务的负载均衡策略、容错机制等,使得在分布式环境中能够更加方便地进行服务的管理和调度。在容器编排工具(如 Kubernetes)中,利用自定义属性可以为容器化应用提供更多的元数据,帮助实现自动化的部署、扩缩容和监控等功能。

在人工智能和机器学习领域,自定义属性可以用于标记数据模型的特征、训练参数以及评估指标等信息。例如,在训练神经网络模型时,通过自定义属性标记不同层的参数设置、激活函数类型等,使得模型的配置和管理更加便捷。在数据预处理阶段,利用自定义属性标记数据的清洗规则、特征工程方法等,有助于提高数据处理的效率和准确性。

在量子计算逐渐兴起的未来,C# 自定义属性或许也能在量子编程中发挥作用。例如,标记量子算法的特性、量子比特的状态以及量子门的操作等信息,为量子计算的开发和优化提供便利。

此外,随着低代码 / 无代码开发平台的发展,自定义属性可以作为一种重要的元数据机制,用于描述组件的行为、属性和事件等。通过在低代码平台中使用自定义属性,开发人员可以更加直观地配置和组合组件,快速构建应用程序,降低开发门槛和成本。

在未来的开发中,我们可以进一步探索自定义属性与其他技术的融合,如区块链、物联网等。在区块链开发中,自定义属性可以用于标记智能合约的权限、交易规则等信息;在物联网应用中,标记传感器数据的类型、精度以及设备的状态等信息。通过不断拓展自定义属性的应用边界,我们能够为各种复杂的开发场景提供更加高效、灵活的解决方案,推动软件开发技术的不断进步。

相关文章:

解锁C#自定义属性:从0到1的深度实践指南

一、开篇&#xff1a;自定义属性的奇妙世界 在 C# 的编程宇宙中&#xff0c;自定义属性是一个强大且迷人的存在&#xff0c;它就像是代码世界里的魔法标记&#xff0c;为我们的程序赋予了丰富的元数据&#xff0c;让代码变得更加智能和富有表现力。 想象一下&#xff0c;你正…...

Ubuntu 安装 OpenCV (C++)

版本详情&#xff1a; Ubuntu: 22.04 5.15.0-133-generic gcc: 11.4.0 g: 11.4.0 OpenCV: 4.7.0 1. 卸载 OpenCV 进入原先编译 opencv 的 build 目录&#xff0c;在该目录下打开终端&#xff0c;执行以下代码&#xff08;如果 build 已经删除了&#xff0c;可以重新编译一…...

deepseek清华大学第二版 如何获取 DeepSeek如何赋能职场应用 PDF文档 电子档(附下载)

deepseek清华大学第二版 DeepSeek如何赋能职场 pdf文件完整版下载 https://pan.baidu.com/s/1aQcNS8UleMldcoH0Jc6C6A?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/3ee62050a2ac...

kill -9 结束某个用户所有进程的方式-linux019

1. 使用 pkill 命令 pkill 命令可以通过用户名直接终止该用户的所有进程。加上 -9 参数&#xff0c;表示强制结束进程。 pkill -9 -u XXXX 说明&#xff1a;这个命令会使用 SIGKILL 信号&#xff08;即 kill -9&#xff09;强制终止 ttlsa 用户的所有进程。 2. 使用 killal…...

十大排序算法

排序算法 插入排序冒泡排序选择排序希尔排序计数排序快速排序1经典 Lomuto 分区法2经典 Lomuto 分区法3随机快排 堆排序归并排序桶排序基数排序 插入排序 从i1开始&#xff0c;判断nums[i-1]和nums[i]的大小&#xff0c;一直到nums[i]插入到自己的位置。模拟抓扑克牌的过程&am…...

第8章作业

接口ip配置 r2 [r2]interface GigabitEthernet 0/0/0 [r2-GigabitEthernet0/0/0]ip address 13.0.0.3 24 [r2-GigabitEthernet0/0/0]interface GigabitEthernet 0/0/1 [r2-GigabitEthernet0/0/1]ip address 100.1.1.254 24 [r2-GigabitEthernet0/0/1]interface GigabitEthernet…...

解决 LeetCode 串联所有单词的子串问题

问题描述 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 &q…...

“深入浅出”系列之QT:(10)Qt接入Deepseek

项目配置&#xff1a; 在.pro文件中添加网络模块&#xff1a; QT core network API配置&#xff1a; 将apiUrl替换为实际的DeepSeek API端点 将apiKey替换为你的有效API密钥 根据API文档调整请求参数&#xff08;模型名称、温度值等&#xff09; 功能说明&#xff1a; 使…...

layui.table.exportFile 导出数据并清除单元格中的空格

Layui在执行数据导出的时候&#xff0c;会出现部分数据单元格中有空格的情况,下面的方法可以去除掉单元格中的空格,供大家参考&#xff01;&#xff01; function table_export(id,title) {//根据传入tableID获取表头var headers $("div[lay-id" id "] .layu…...

AIRIOT智慧消防管理解决方案

随着科技的飞速发展和城市化进程的不断推进&#xff0c;消防安全问题逐渐凸显。传统消防手段在应对现代复杂多变的火灾事故时显得力不从心&#xff0c;面临着诸多挑战和弊端&#xff1a; 监控不足&#xff1a;传统消防依赖人力和烟感器等设备&#xff0c;导致监控范围有限、反应…...

使用VSCODE开发C语言程序

使用vscode配置C语言开发环境 一、安装VSCODE 1、下载vscode ​ 从官方网站&#xff08;https://code.visualstudio.com/Download&#xff09;上&#xff0c;下载windows版本的vscode 2、安装vscode ​ 下载完毕后&#xff0c;按照提示进行安装即可&#xff08;尽可能不要安…...

Jenkins 创建 Node 到 Windows

Jenkins 创建 Node 到 Windows 一. 新建 Node Dashboard -> Manage Jenkins -> Manage Nodes and Clouds Dashboard -> Nodes -> New Node 二. 配置节点 Node&#xff1a;节点名 Description&#xff1a;节点描述 Number of executors&#xff1a;节点最大同…...

鸿蒙状态管理概述

状态管理 状态管理之v1LocalStorageLocalStorageLink的框架行为LocalStorageProp的框架行为LocalStorage使用场景 AppStorageStorageLink的框架行为StorageProp的框架行为AppStorage的使用场景 PersistentStorageEnvironmentEnvironment内置参数 WatchWatch的使用场景 $$语法$$…...

英文字体:极简现代浓缩未来派科技海报标题排版无衬线字体 PODIUM Sharp Font

PODIUM Sharp 是 2012 年设计的 DUDU 字体的扩展版本。多年后&#xff0c;我决定通过添加新的母版和粗细来重建和开发这种字体。最后&#xff0c;PODIUM Sharp 由 234 种款式组成&#xff1a;从超压缩发际线到超扩展重度。 这个项目的主要目的是在我在旧波兰标本中发现的不同模…...

Linux系统配置阿里云yum源,安装docker

配置阿里云yum源 需要保证能够访问阿里云网站 可以先ping一下看看&#xff08;阿里云可能禁ping&#xff0c;只要能够解析为正常的ip地址即可&#xff09; ping mirrors.aliyun.com脚本 #!/bin/bash mkdir /etc/yum.repos.d/bak mv /etc/yum.repos.d/*.repo /etc/yum.repos…...

基础排序算法

冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;一种交换排序&#xff0c;它的基本思想是&#xff1a;两两比较相邻记录的关键字&#xff0c;如果反序则交换&#xff0c;直到没有反序的记录为止。 以下代码是改进的冒泡算法&#xff0c;在排序好了之后可以直接跳出循环…...

蓝桥杯备赛1-2合法日期

题目描述 小蓝正在上小学&#xff0c;老师要求同学们在暑假每天记日记。可是小蓝整个暑假都在玩&#xff0c;直到最后一天才想起要记日记。于是小蓝赶紧编了一些日记交给老师。 没想到&#xff0c;日记很快就被老师发现了问题&#xff0c;原来小蓝记完 8 月 31 日的日记&…...

巧用GitHub的CICD功能免费打包部署前端项目

近年来&#xff0c;随着前端技术的发展&#xff0c;前端项目的构建和打包过程变得越来越复杂&#xff0c;占用的资源也越来越多。我有一台云服务器&#xff0c;原本打算使用Docker进行部署&#xff0c;以简化操作流程。然而&#xff0c;只要执行sudo docker-compose -f deploy/…...

后端开发-分页游标设计(解决大数据量分页查询时的性能问题)

文章目录 适用场景/背景描述本质原理简单举例子分页游标&#xff0c;若用非唯一索引的字段排序分页游标&#xff0c;若 order by 的不是索引还能使用分页游标吗 适用场景/背景描述 如果需要做分页查询且被查询的数据量很大&#xff0c;直接 limit offset 有可能会导致全表扫描…...

SpringBoot 框架关于如何创建并使用 Redis 的详细介绍

本文是博主整理项目时整理出来的&#xff0c;项目使用 SpringBoot 框架&#xff0c;使用 Redis 作为缓存组件&#xff0c;用于缓存部分热点接口数据。 文章目录 一、Redis 的前置配置1、引入依赖2、单机配置3、集群配置4、两种配置路径解读 二、创建 Redis 配置类1、简易版 Red…...

跨语言语义理解与生成:多语言预训练方法及一致性优化策略

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…...

旧手机热点无法提供ipv6解决方法(emui 8 热点提供ipv6)

旧手机热点无法提供ipv6解决方法 手机&#xff1a;荣耀8x 系统版本: EMUI 8 网络&#xff1a;移动流量卡 解决方案 设置-》无线和网络-》移动网络-》接入点名称(APN)-》cmiot 修改 APN协议: IPv4/IPv6 修改 APN漫游协议: IPv4/IPv6...

第2章 信息技术发展(一)

2.1 信息技术及其发展 2.1.1 计算机软硬件 计算机硬件(Computer Hardware)是指计算机系统中由电子、机械和光电元件等组成的各种物理装置的总称。 计算机软件 (Computer Software)是指计算机系统中的程序及其文档&#xff0c;程序是计算任务的处理对象和处理规则的描述; 文档…...

循环神经网络RNN原理与优化

目录 前言 RNN背景 RNN原理 上半部分&#xff1a;RNN结构及按时间线展开图 下半部分&#xff1a;RNN在不同时刻的网络连接和计算过程 LSTM RNN存在的问题 LSTM的结构与原理 数学表达层面 与RNN对比优势 应用场景拓展 从简易但严谨的代码来看RNN和LSTM RNN LSTM 前言 绕循环神经…...

Redis常见数据结构及其在项目中用法

Redis 可以使用 String 数据类型和 INCR 命令来统计访问量。 以下是在 Redis 控制台中统计访问量的示例&#xff1a; 设置计数器&#xff1a; SET page_views 0这将创建一个名为 page_views 的键&#xff0c;并将其初始值设置为 0。如果该键已经存在&#xff0c;则会覆盖其值。…...

DeepSeek 基础使用文档介绍

目录 1. 简介 2. 安装与验证 安装 验证安装 3. 基础用法 3.1 数据搜索 3.2 数据预处理 3.3 机器学习优化 4. 进阶用法 4.1 并行计算 4.2 高级搜索功能 4.3 自定义数据处理管道 4.4 提问功能 5. 文档和代码分析 5.1 文档解析 5.2 代码分析 6. 实际案例 案例描…...

微信小程序(uni)+蓝牙连接+Xprint打印机实现打印功能

1.蓝牙列表实现&#xff0c;蓝牙设备展示&#xff0c;蓝牙连接 <template><view class"container"><view class"container_top"><view class"l">设备名称</view><view class"r">{{state.phoneNam…...

数仓搭建(hive):DWB层(基础数据层)

维度退化: 通过减少表的数量和提高数据的冗余来优化查询性能。 在维度退化中&#xff0c;相关的维度数据被合并到一个宽表中&#xff0c;减少了查询时需要进行的表连接操作。例如&#xff0c;在销售数据仓库中&#xff0c;客户信息、产品信息和时间信息等维度可能会被合并到一…...

Mybatis的#{}和${}

#{}:预编译语句,用?对参数位置进行一个占位的操作,在数据库生成一个模版,等待后续填充.也可以推测出#在生成模版后的性能是比$快的. ${}:即时语句,提前的吧参数填充进去,在MySQL里就是一个完整的SQL语句. 填充逻辑不同 #{}会给String类型的参数自动的加上双引号,而${}则是直…...

@Transactional 嵌套,内层抛异常,外层用 try-catch 捕获但实际事务却回滚了

1. 内层事务传播行为为 Propagation.REQUIRED 且设置了回滚标记 虽然外层捕获了异常&#xff0c;但内层事务在抛出异常时可能已经标记了整个事务需要回滚。在 Spring 中&#xff0c;当内层事务传播行为为 Propagation.REQUIRED 时&#xff0c;它和外层事务属于同一个逻辑事务。…...

在实时大数据处理中如何平衡延迟和吞吐量

在实时大数据处理中&#xff0c;平衡延迟和吞吐量是一个至关重要的挑战。以下是一些实用的策略和技巧&#xff0c;有助于在这两者之间找到最佳平衡点&#xff1a; 一、技术层面的平衡策略 并行处理&#xff1a; 通过同时处理多个任务来提高吞吐量。在实时大数据处理环境中&am…...

vue学习六

十三 路由 安装 Vue Router | Vue.js 的官方路由 vue3安装vue-router 4版本;vue2安装vue-router 3版本。 测试使用vue3&#xff0c;命令不带版本号默认安装4。 npm install vue-router4 使用 定义 定义文件&#xff1a;src\router\index.ts import {createRouter,crea…...

【Content-Type详解、Postman中binary格式、json格式数据转原始二进制流等】

Content-Type详解、Postman中binary格式、json格式数据转原始二进制流等 背景&#xff1a;postman中如何使用binary格式上传文件 Content-TypeContent-Type的格式由三部分组成&#xff1a;以下是一些常见的Content-Type示例&#xff1a; Postman中 binary格式定义&#xff1a;用…...

K8S下redis哨兵集群使用secret隐藏configmap内明文密码方案详解

#作者&#xff1a;朱雷 文章目录 一、背景环境及方案说明1.1、环境说明1.2、方案一&#xff1a;使用配置文件设置密码1.3、方案二&#xff1a;使用args 的命令行传参设置密码 二、redis secret configmap deployment参考2.1 创建secret-redis.yaml参考2.2 修改configmap配置参…...

插槽slot

在 Vue 中&#xff0c;插槽&#xff08;slot&#xff09;主要用于实现父组件向子组件传递内容&#xff0c;是一种典型的父传子的方式 元素是一个插槽出口 (slot outlet)&#xff0c;标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。 默认插槽 在外部没有提供任何…...

BFS 解决 FloodFill 算法(典型算法思想)—— OJ例题算法解析思路

目录 一、733. 图像渲染 - 力扣&#xff08;LeetCode&#xff09; 算法代码&#xff1a; 算法思路 基础参数 函数入口 检查条件 初始化 BFS BFS 填充过程 返回结果 复杂度分析 总结 二、200. 岛屿数量 - 力扣&#xff08;LeetCode&#xff09; 算法代码&#xff1a;…...

8 SpringBootWeb案例(上): 查询【分页功能(分页插件)】、删除、新增、修改

文章目录 前言:SpringBootWeb案例1. 准备工作1.1 需求&环境搭建1.1.1 需求说明1.1.2 环境搭建1.2 开发规范1.2.1 开发规范-REST(不强求非要这种风格,传统风格有时候更方便)1.2.2 开发规范-统一响应结果和异常处理1.2.3 开发流程2. 部门管理2.1 查询部门2.1.1 原型和需求…...

Docker 部署 Dify:轻松集成 Ollama 和 DeepSeek

1 Ollama的安装及使用 1.1 什么是Ollama&#xff1f; Ollama 是一个用于本地部署和运行大型语言模型的框架。 Ollama 的作用包括&#xff1a; 本地模型运行&#xff1a;Ollama 允许在本地机器上运行大型语言模型&#xff08;如 LLaMA、DeepSeek 等&#xff09;&#xff0c;无…...

深入解析MySQL索引:本质、分类、选择及使用原则

一、索引的本质 索引&#xff0c;作为数据库中的一种核心数据结构&#xff0c;其本质在于通过改变数据结构来加快查询效率。可以将索引理解为数据库中的一种“目录”或“路标”&#xff0c;它帮助数据库系统快速定位到需要查询的数据行&#xff0c;从而大大提高数据检索的速度…...

MYSQL的第一次

目录 前情提要 题目解析 连接并使用数据库 创建employees表 创建orders表? 创建invoices表?? ?查看建立的表 前情提要 需要下载mysql并进行配置&#xff0c;建议下载8.0.37&#xff0c;详情可见MySQL超详细安装配置教程(亲测有效)_mysql安装教程-CSDN博客 题目解析…...

机器学习笔记——常用损失函数

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记介绍机器学习中常见的损失函数和代价函数&#xff0c;各函数的使用场景。 热门专栏 机器学习 机器学习笔记合集 深度学习 深度学习笔记合集 文章目录 热门…...

谷粒商城学习笔记-13-配置git-ssh-配置代码免密提交

安装配置 git 1 、下载 git &#xff1a; https://git-scm.com 下载git客户端&#xff0c;右键桌面Git GUI/bash Here。进入bash 2 、配置 git &#xff0c;进入 git bash # 配置用户名 git config --global user.name "username" //(名字&#xff0c;随意写)# …...

51单片机学习——静态数码管显示

1. 数码管介绍 LED数码管&#xff1a;数码管是一种简单、廉价的显示器&#xff0c;是由多个发光二极管封装在一起组成“8”字型的器件。 2. 数码管引脚定义 LED的阴极连接到一个端口上是共阴极连接&#xff0c;称为共阴极数码管。LED的阳极连接到一个端口上是共阳极连接&…...

Qt5 C++ TcpSocket 如何判断是服务主动断开tcp socket连接?

文章目录 实现思路示例代码代码解释主要功能和用法注意事项 在 Qt 5.9.9 的 C 开发中&#xff0c;使用 QTcpSocket 时&#xff0c;要判断是服务端主动断开 TCP Socket 连接&#xff0c;可以通过处理 QTcpSocket 的 disconnected 信号&#xff0c;结合 QTcpSocket 的状态以及…...

UEFI Spec 学习笔记---9 - Protocols — EFI Loaded Image

本节定义EFI_LOADED_IMAGE_PROTOCOL和 EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL。这些协议分别描述了已加载到内存中的映像&#xff0c;并指定了PE/COFF映像通过EFI引导服务LoadImage()加载 时使用的设备路径。这些描述包括 load image 的源、映像在内存中的当前位置、为image分…...

Deepseek 与 ChatGPT:AI 浪潮中的双子星较量

引言 在人工智能飞速发展的当下&#xff0c;AI 语言模型成为了人们关注的焦点。Deepseek 与 ChatGPT 作为其中的佼佼者&#xff0c;各自展现出独特的魅力&#xff0c;引领着 AI 技术的发展潮流。今天&#xff0c;就让我们深入探讨这两款模型&#xff0c;看看它们在 AI 领域中是…...

open-webui安装

docker安装openwebui 拉取镜像 docker pull ghcr.io/open-webui/open-webui:maindocker images启动 docker run -d -p 8346:8080 --name open-webui ghcr.io/open-webui/open-webui:maindocker ps查看端口占用 lsof -i:8346访问地址 http://ip:port http://127.0.0.1:8346...

docker 安装jenkins

使用docker 容器安装jenkins比较方便&#xff0c;但是细节比较重要&#xff0c;这里实战安装了一遍&#xff0c;可用&#xff1a; 拉取最新的jenkins镜像 docker pull jenkins/jenkins 如果没有翻墙的话&#xff0c;可以会有下面的报错&#xff1a; Error response from dae…...

前端利器:DateUtils 日期时间工具深度剖析

序言 在前端开发中&#xff0c;日期和时间的处理是一个常见且重要的任务。从格式化日期展示到复杂的日期计算&#xff0c;每一个环节都需要精确和高效的代码支持。今天&#xff0c;我们就来深入探讨一个精心打造的前端日期时间工具类 DateUtils&#xff0c;它涵盖了从基础的日…...

Denoising Diffusion Restoration Models论文解读

论文要点 恢复的线性逆问题可以使用预训练的DDPM完成&#xff1a;1. 将降质矩阵使用SVD&#xff0c;得到分解矩阵&#xff1b;2. 使用分解矩阵将图像投影到降质类型间共享的谱空间&#xff1b;3. 谱空间中执行DDPM。 评价 同Track的方法同样很多&#xff0c;比如后续的DDNM、…...