MVVM框架下的Mapster工具包

目录

引言

高性能对象映射框架 Mapster

1、简单映射

2、数据类型

2.1、基本类型

2.2、枚举类型

2.3、字符串类型

2.4、集合

2.5、可映射对象

3、映射配置

3.1、全局设置

3.2、基于条件的映射

3.3、未知源类型

3.4、泛型类型

4、映射配置的继承

4.1、源类型映射配置子类默认继承

 4.2、目标类型映射配置默认不继承

4.3、手动继承

5、配置实例

5.1、不同场景独立配置

 5.2、映射配置的复制与扩展

 5.3、配置入口和遗漏补充

6、不同程序集的映射配置

6.1、Scan

6.2、Apply

 7、映射验证和编译

7.1、验证映射配置是否存在错误

7.2、编译配置

8、自定义映射

8.1、自定义属性映射关系

8.2、条件映射

8.3、映射私有成员

8.4、多级映射

8.5、空值

8.6、多个源映射到同一个类型

 8.7、特性

AdaptIgnore 特性


引言

WPF应用程序的三层架构:Model->ViewModel->View。个人理解就是将后台的运行配置文件或则结果Model转换为可以与窗体交互的ViewModel,ViewModel 再与窗体进行深度绑定,实现后台与前端的解耦。ViewModel作为中间介质包含Model的开放属性,同时又有和View交互的衍生属性或则命令Command。Mapster提供了一种Model<->ViewModel的映射方法,通过Adapt扩展方法实现属性相互映射。

高性能对象映射框架 Mapster

Mapster的高性能可由以下对比列表得出:

MethodMeanStdDevErrorGen 0Gen 1Gen 2Allocated
'Mapster 6.0.0'108.59 ms1.198 ms1.811 ms31000.0000--124.36 MB
'Mapster 6.0.0 (Roslyn)'38.45 ms0.494 ms0.830 ms31142.8571--124.36 MB
'Mapster 6.0.0 (FEC)'37.03 ms0.281 ms0.472 ms29642.8571--118.26 MB
'Mapster 6.0.0 (Codegen)'34.16 ms0.209 ms0.316 ms31133.3333--124.36 MB
'ExpressMapper 1.9.1'205.78 ms5.357 ms8.098 ms59000.0000--236.51 MB
'AutoMapper 10.0.0'420.97 ms23.266 ms35.174 ms87000.0000--350.95 MB

对比其他框架占用更少的内存, 拥有更好的性能。

1、简单映射

默认会将两个类型中相同名称的属性进行映射

//映射到新对象
var destObject = sourceObject.Adapt<Destination>();
//映射到现有对象
sourceObject.Adapt(destObject);

扩展方法 Adapt,为避免装箱/拆箱 请使用第一个版本指定类型

var dest = src.Adapt<TSource, TDestination>();
var dest = src.Adapt<TDestination>();

2、数据类型

2.1、基本类型

基本类型的转换 ,例如: int/bool/dobule/decimal ,包括可空的基本类型。

只要C#支持类型转换的类型,那么在 Mapster 中也同样支持转换。

decimal i = 123.Adapt<decimal>(); //equal to (decimal)123

2.2、枚举类型

Mapster 会自动把枚举映射到数字类型,同样也支持 字符串到枚举 和 枚举到字符串的映射。

.NET 默认实现 枚举/字符串 转换非常慢,Mapster 比 .NET 的默认实现快两倍。

在 Mapster 中,字符串转枚举,如果字符串为空或空字符串,那么枚举将初始化为第一个枚举值。

在Mapster中,也支持标记的枚举。

var e = "Read, Write, Delete".Adapt<FileShare>();  
//FileShare.Read | FileShare.Write | FileShare.Delete

对于不同类型的枚举,Mapster 默认将值映射为枚举。调用 EnumMappingStrategy 方法可以指定枚举映射方式,如:

TypeAdapterConfig.GlobalSettings.Default
    .EnumMappingStrategy(EnumMappingStrategy.ByName);

2.3、字符串类型

在 Mapster 中,将其它类型映射为字符串时,Mapster 将调用类型的 ToString 方法。

如果将字符串映射为类型时,Mapster 将调用类型的 Parse 方法。

var s = 123.Adapt<string>(); // 等同于: 123.ToString();
var i = "123".Adapt<int>();  // 等同于: int.Parse("123");

2.4、集合

包括列表、数组、集合、包括各种接口的字典之间的映射: IList<T>, ICollection<T>, IEnumerable<T>, ISet<T>, IDictionary<TKey, TValue> 等等…

var list = db.Pocos.ToList();
var target = list.Adapt<IEnumerable<Dto>>();  

2.5、可映射对象

Mapster 可以使用以下规则映射两个不同的对象

  • 源类型和目标类型属性名称相同。 例如: dest.Name = src.Name
  • 源类型有 GetXXXX 方法。例如: dest.Name = src.GetName()
  • 源类型属性有子属性,可以将子属性的赋值给符合条件的目标类型属性,例如: dest.ContactName = src.Contact.Namedest.Contact_Name = src.Contact.Name
class Staff {
    public string Name { get; set; }
    public int GetAge() { 
        return (DateTime.Now - this.BirthDate).TotalDays / 365.25; 
    }
    public Staff Supervisor { get; set; }
    ...
}

struct StaffDto {
    public string Name { get; set; }
    public int Age { get; set; }
    public string SupervisorName { get; set; }
}

var dto = staff.Adapt<StaffDto>();  
//dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name

可映射对象类型包括:

  • 结构体
  • 接口
  • 实现 IDictionary<string, T> 接口的字典类型
  • Record 类型 (类、结构体、接口)

对象转换为字典的例子:

var point = new { X = 2, Y = 3 };
var dict = point.Adapt<Dictionary<string, int>>();
dict["Y"].ShouldBe(3);

Record 类型的例子:

class Person {
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age) {
        this.Name = name;
        this.Age = age;
    }
}

var src = new { Name = "Mapster", Age = 3 };
var target = src.Adapt<Person>();

自动映射 Record 类型有一些限制:

  • Record 类型属性必须没有 set
  • 只有一个非空构造函数
  • 构造函数中的所有参数名称必须与属性名称相同

如果不符合以上规则,需要增加额外的 MapToConstructor 配置

3、映射配置

使用 TypeAdapterConfig<TSource, TDestination>.NewConfig()TypeAdapterConfig<TSource, TDestination>.ForType() 配置类型映射;

当调用 NewConfig 方法时,将会覆盖已存在的类型映射配置。

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Ignore(dest => dest.Age)
    .Map(dest => dest.FullName,
        src => string.Format("{0} {1}", src.FirstName, src.LastName));

如若不想覆盖之前已经创建好的映射配置,可以使用 TypeAdapterConfig<TSource, TDestination>.ForType()

ForType 方法与 NewConfig 的差别:如果指定类型映射配置不存在,那它将创建一个新的映射,如果指定类型的映射配置已存在,那么它将会扩展已有的映射配置,而不是删除或替换已有的映射配置。

TypeAdapterConfig<TSource, TDestination>
        .ForType()
        .Ignore(dest => dest.Age)
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

3.1、全局设置

使用全局设置将映射策略应用到所有的映射配置。

TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true);

对于特定的类型映射,你可以使用 TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig() 覆盖全局映射配置。

TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig().PreserveReference(false);

3.2、基于条件的映射

你可以使用 When 方法,当满足某个条件时,进行一些特定的映射操作。

下面的这个例子,当任何一个映射的 源类型和目标类型 相同时,不映射 Id 属性:

TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType)
    .Ignore("Id");

在下面这个例子中,映射配置只对 IQueryable 生效:

TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection)
    .IgnoreAttribute(typeof(NotMapAttribute));

3.3、未知源类型

在不确定源类型的时候,使用 ForDestinationType 来创建针对于 目标类型 的映射配置。

比如使用 AfterMapping 在映射完成后调用目标类型对象的 Validate 方法:

TypeAdapterConfig.GlobalSettings.ForDestinationType<IValidator>()
                    .AfterMapping(dest => dest.Validate());

注意!在上面的代码段中指定目标类型为 IValidator 接口,那么将会把映射配置应用到所有实现了 IValidator 的类型。

3.4、泛型类型

如果映射的是泛型类型,可以通过将泛型类型传给 ForType 来创建设置.

TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
    .Map("value", "Value");

4、映射配置的继承

4.1、源类型映射配置子类默认继承

Mapster 默认会把 源类型的 映射配置 应用到 源类型的子类。

如创建了一个 SimplePoco -> SimpleDto 的映射配置:

TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig()
    .Map(dest => dest.Name, src => src.Name + "_Suffix");

那么继承了 SimplePocoDerivedPoco 也将应用同样的映射配置:

var dest = TypeAdapter.Adapt<DerivedPoco, SimpleDto>(src); 
//dest.Name = src.Name + "_Suffix"

如果不希望子类使用父类映射配置,可以设置 AllowImplicitSourceInheritancefalse 关闭继承:

TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false;

 4.2、目标类型映射配置默认不继承

Mapster 默认不会把 目标类型的 映射配置 应用到 目标类型的子类。

可以设置 AllowImplicitDestinationInheritance 开启:

TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true;

4.3、手动继承

可以通过 Inherits 方法显示的继承类型映射配置:

TypeAdapterConfig<DerivedPoco, DerivedDto>.NewConfig()
        .Inherits<SimplePoco, SimpleDto>();

父类也可继承子类的映射关系

TypeAdapterConfig<Vehicle, VehicleDto>.NewConfig()
    .Include<Car, CarDto>();

Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" };
var dto = vehicle.Adapt<Vehicle, VehicleDto>();

dto.ShouldBeOfType<CarDto>();
((CarDto)dto).Make.ShouldBe("Toyota"); //The 'Make' property doesn't exist in Vehicle

5、配置实例

5.1、不同场景独立配置

在 Mapster 中,默认的配置实例为 TypeAdapterConfig.GlobalSettings ,如果需要在不同场景下有不同的映射配置,Mapster 提供了 TypeAdapterConfig 用于实现此需求:

var config = new TypeAdapterConfig();
config.Default.Ignore("Id");

如何给 配置实例 添加l类型映射配置?

直接使用 NewConfigForType 方法即可:

config.NewConfig<TSource, TDestination>()
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

config.ForType<TSource, TDestination>()
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

注意! 配置实例 在程序中一定要作为单例存在,否则会影响性能! 

通过将 配置实例 作为参数传给 Adapt 方法来应用特定的类型映射配置:

var result = src.Adapt<TDestination>(config);

也可以创建一个指定 TypeAdapterConfigMapper,使用 Mapper 来做映射:

var mapper = new Mapper(config);
var result = mapper.Map<TDestination>(src);

 5.2、映射配置的复制与扩展

如果想从现有的配置中创建配置实例,可以使用 Clone 方法。

例如 复制全局配置实例 :

var newConfig = TypeAdapterConfig.GlobalSettings.Clone();
var newConfig = oldConfig.Clone();

Fork 方法内部直接调用 Clone 方法,但是使用 Fork 方法的形式与使用 Clone 方法有些许差别。

 Fork 可以内部直接进行映射扩展

var forked = mainConfig.Fork(config => 
				{
					config.ForType<Poco, Dto>()
						.Map(dest => dest.code, src => src.Id);
				});

var dto = poco.Adapt<Dto>(forked);

以上代码等同于使用 Clone 方法实现的以下代码:

var forked = mainConfig.Clone();
forked.ForType<Poco, Dto>()
      .Map(dest => dest.code, src => src.Id);

var dto = poco.Adapt<Dto>(forked);

 5.3、配置入口和遗漏补充

映射配置应该只初始化并且只进行一次配置。因此在编写代码的时候不能将映射配置和映射调用放在同一个地方。多次设置同一个配置会抛出异常,如下:

config.ForType<Poco, Dto>().Ignore("Id");
var dto1 = poco1.Adapt<Dto>(config);

config.ForType<Poco, Dto>().Ignore("Id"); //<--- 这里将抛出异常,因为在这之前已经触发过了映射
var dto2 = poco2.Adapt<Dto>(config);

一般会放在程序的入口,如 Main方法 Startup方法

但是难免在使用时会对映射配置进行补充,这时可通过Fork函数进行补充,不用担心性能问题,只有第一次使用配置实例时会编译,之后的调用将从缓存中获取。

var dto = poco.Adapt<Dto>(
	config.Fork(forked => forked.ForType<Poco, Dto>().Ignore("Id"));

6、不同程序集的映射配置

将映射配置分布在许多不同的程序集上是比较常见的。

6.1、Scan

允许扫描程序集中的这些规则会很有帮助,这样你就有一些基本的方法来组织规则,并且不会忘记调用注册码。在某些情况下,甚至可能需要按特定顺序注册程序集,以便某些规则优先于其他规则。装配扫描有助于实现这一点。

程序集扫描很简单,只需在程序集中创建任意数量的实现,然后从类中调用:IRegisterScanTypeAdapterConfig

public class MyRegister : IRegister
{
	public void Register(TypeAdapterConfig config)
	{
		config.NewConfig<TSource, TDestination>();

		//OR to create or enhance an existing configuration
		config.ForType<TSource, TDestination>();
	}
}

要在全局级别进行扫描和注册,请执行以下操作:

TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN)

对于特定的配置实例:

var config = new TypeAdapterConfig();
config.Scan(assembly1, assembly2, assemblyN);

6.2、Apply

如果使用其他程序集扫描库(如MEF),则可以使用Apply方法轻松应用注册。

var registers = container.GetExports<IRegister>();
config.Apply(registers);

Apply方法还允许您有选择地从一个或多个而不是每个组件中进行拾取。

var register = new MockingRegister();
config.Apply(register);

 7、映射验证和编译

7.1、验证映射配置是否存在错误

调用 TypeAdapterConfig<Source, Destination>.NewConfg()Compile 方法将验证 特定类型的映射配置是否存在错误;

调用 配置实例 的 Compile 方法以验证 配置实例中的映射配置 是否存在错误;

另外,如果启用了 显式映射 , 它还将包含没有在映射器中注册的类的错误。

// 验证特定配置
var config = TypeAdapterConfig<Source, Destination>.NewConfig();
config.Compile();

// 验证整个配置实例的配置
TypeAdapterConfig<Source, Destination>.NewConfig();
TypeAdapterConfig<Source2, Destination2>.NewConfig();
TypeAdapterConfig.GlobalSettings.Compile();

7.2、编译配置

Mapster 默认将在第一次调用映射时自动编译:

var result = poco.Adapt<Dto>();

你也可以通过调用 配置实例 或 特定映射配置的Compile 方法编译映射:

// 全局配置实例
TypeAdapterConfig.GlobalSettings.Compile();

// 配置实例
var config = new TypeAdapterConfig();
config.Compile();

// 特定配置
var config = TypeAdapterConfig<Source, Destination>.NewConfig();
config.Compile();

推荐在程序添加映射配置完成后调用一次 Compile 方法,可以快速验证 映射配置中是否存在错误,而不是在运行到某一行业务代码时触发错误降低效率。

注意!调用 Compile 方法前应该完成所有的映射配置,调用 Compile 方法之后 配置实例 就不允许添加修改其它映射配置!

8、自定义映射

8.1、自定义属性映射关系

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Map(dest => dest.FullName,
        src => string.Format("{0} {1}", src.FirstName, src.LastName));
TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Map(dest => dest.Gender,      // dest.Gender: Genders.Male 或 Genders.Female 枚举类型
        src => src.GenderString); // src.GenderString: "Male" 或 "Female" 字符串类型

8.2、条件映射

可以通过设置 Map 方法的第三个参数,实现在 源对象 满足某些条件下进行映射;

当存在多个条件的情况下,Mapster 会依次往下执行判断条件是否满足,直到满足条件为止;

当找不到满足条件的映射时,将分配空值或默认值:

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Map(dest => dest.FullName, src => "Sig. " + src.FullName, srcCond => srcCond.Country == "Italy")
    .Map(dest => dest.FullName, src => "Sr. " + src.FullName, srcCond => srcCond.Country == "Spain")
    .Map(dest => dest.FullName, src => "Mr. " + src.FullName);

 使用 IgnoreIf 方法,当满足条件时将忽略此成员的映射:

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .IgnoreIf((src, dest) => !string.IsNullOrEmpty(dest.Name), dest => dest.Name);

 Mapster 默认映射时会将 源对象的所有成员映射到目标对象,如果不想映射空值,那么可以使用 IgnoreNullValues 方法进行配置:

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .IgnoreNullValues(true);

 注意!如果想要在满足条件时跳过映射,应该使用 IgnoreIf,详情可参阅

8.3、映射私有成员

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Map("PrivateDestName", "PrivateSrcName");

8.4、多级映射

TypeAdapterConfig<Poco, Dto>.NewConfig()
    .Map(dest => dest.Child.Name, src => src.Name);

 映射 Dto 中属性的值到 Poco

public class Poco
{
    public string Name { get; set; }
    public string Extra { get; set; }
}

public class Dto
{
    public string Name { get; set; }
    public SubDto SubDto { get; set; }
}
public class SubDto
{
    public string Extra { get; set; }
}

如果想将 Dto 中的所有属性和 Dto.SubDto 中的所有属性映射到 Poco,那么可以通过配置 dto.SubDto 映射到 Poco 来实现:

TypeAdapterConfig<Dto, Poco>.NewConfig()
    .Map(poco => poco, dto => dto.SubDto);

8.5、空值

如果 src.Childnull,那么映射到 dest.Name 的配置不会抛出 NullPointerException ,而是映射空值:

TypeAdapterConfig<Poco, Dto>.NewConfig()
    .Map(dest => dest.Name, src => src.Child.Name);

8.6、多个源映射到同一个类型

如果想将 Dto1Dto2两个类型映射到 Poco 类型,那么可以通过将 Dto1 Dto2 包装成一个 tuple,然后将 tuple.Item1tuple.Item2 映射到 Poco 来实现:

TypeAdapterConfig<(Dto1, Dto2), Poco>.NewConfig()
    .Map(dest => dest, src => src.Item1)
    .Map(dest => dest, src => src.Item2);

 8.7、特性

AdaptIgnore 特性

当一个属性有 [AdaptIgnore] 标记时,这个属性将不会被映射:

public class Product {
    public string Id { get; set; }
    public string Name { get; set; }

    [AdaptIgnore]
    public decimal Price { get; set; }
}

当一个成员有 [AdaptIgnore] 标记时,不管是 源到目标 还是 目标到源 的映射都将会忽略这个成员,可以使用 MemberSide 指定单方的忽略。

例如,只有 Product 当作 源映射时,Price 字段才会被忽略:

public class Product {
    public string Id { get; set; }
    public string Name { get; set; }

    [AdaptIgnore(MemberSide.Source)]
    public decimal Price { get; set; }
}

AdaptMember 特性标记

使用 [AdaptMember] 特性标记可以实现修改映射的名称。

例如,将 Id 映射为 Code:

public class Product {
    [AdaptMember("Code")]
    public string Id { get; set; }
    public string Name { get; set; }
}

使用 [AdaptMember] 特性标记可以实现映射非公开成员:

public class Product {
    [AdaptMember]
    private string HiddenId { get; set; }
    public string Name { get; set; }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/605181.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Lora基础炼丹学习笔记

1、收集数据集 20-30张人物各个角度、各个姿势的图片 2、图片预处理 裁剪 打标签 裁剪必须也要512 * 512 &#xff0c;因为sd1.5就是用这个尺寸训练的&#xff0c;可以使用后期处理 打标可以勾选这个&#xff0c;Deepbooru对二次元画风更友好 打标也可以使用wb14-tagger的…

Centos7 安装 MySQL5.7 使用 RPM 方式

1 访问网站 https://downloads.mysql.com/archives/community/ 选择合适的版本&#xff0c;点击 Download。 2 上传下载好的 mysql-5.7.44-1.el7.x86_64.rpm-bundle.tar 文件到 Centos7 机器&#xff0c;这里放到了 下载 目录。 3 解压 mysql-5.7.44-1.el7.x86_64.rpm-bundle.…

力扣每日一题119:杨辉三角||

题目 简单 给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0 输出: [1]示例 3: 输入: rowIndex 1 输出…

如何用多个高斯泼溅合成新的场景【3DGS】

3D高斯泼溅&#xff08;3D Gaussian Splatting&#xff09;作为一种突破性摄影测量和可视化技术作为 SIGGRAPH 2023 上发表的研究论文的一部分发布。我相信3DGS是允许像你我这样的日常用户扫描 3D 的最佳现代方法并保留有机材料的精细细节&#xff0c;尤其是植物、树木、花卉和…

【青龙面板教程】保姆级拉库 Faker库 以及依赖安装教程

青龙面板最新版拉库教程 新版青龙&#xff08;订阅&#xff09;拉库教程 拉库前请打开青龙面板-配置文件 第18行 GithubProxyUrl"" 双引号中的内容清空复制以下拉库命令即可。Faker2 助力池版【安全本地sign防CK泄漏】使用助力池请在群里发"助力池" 机器…

初阶数据结构之单链表详解

目录 一&#xff1a;单链表概念 二&#xff1a;单链表的基本操作 1.定义结点 2.创建链表&#xff08;初始化链表&#xff09; 3:新增结点 4.单链表尾插 5.单链表头插 6.单链表尾删 7&#xff1a;单链表头删 8.打印单链表 9.查找单链表结点 10.单链表删除指定结点 1…

【C语言】static关键字用法

目录 一、static修饰局部变量 二、static修饰全局变量 三、static修饰函数 一、static修饰局部变量 首先我们来看两段代码: 代码1&#xff08;不加static&#xff09; #include <stdio.h> void test() {int i 0;i;printf("%d ", i); } int main() {int i…

UE5材质基础(2)——数学节点篇

UE5材质基础&#xff08;2&#xff09;——数学节点篇1 目录 UE5材质基础&#xff08;2&#xff09;——数学节点篇1 Add节点 Append节点 Abs节点 Subtract节点 Multiply节点 Divide节点 Clamp节点 Time节点 Lerp节点 Add节点 快捷键&#xff1a;A鼠标左键 值相加…

C++学习第十二天(继承)

1、继承的概念以及定义 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行拓展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结构&#x…

EditReady for Mac激活版:专业视频转码工具

对于视频专业人员来说&#xff0c;一款高效的视频转码工具是不可或缺的。EditReady for Mac正是这样一款强大的工具&#xff0c;它拥有简洁直观的操作界面和强大的功能&#xff0c;让您的视频处理工作事半功倍。 EditReady for Mac支持多种视频格式的转码&#xff0c;并且支持常…

多线程学习Day09

10.Tomcat线程池 LimitLatch 用来限流&#xff0c;可以控制最大连接个数&#xff0c;类似 J.U.C 中的 Semaphore 后面再讲 Acceptor 只负责【接收新的 socket 连接】 Poller 只负责监听 socket channel 是否有【可读的 I/O 事件】 一旦可读&#xff0c;封装一个任务对象&#x…

阿里云VOD视频点播流程(2)

二、视频点播 1、入门代码 基于OSS原生SDK上传 &#xff0c;参考文档&#xff1a;https://help.aliyun.com/zh/vod/user-guide/upload-media-files-by-using-oss-sdks?spma2c4g.11186623.0.0.1f02273fj4lxNJ 视频点播面向开发者提供了丰富的上传方式&#xff0c;其中上传SDK&…

软件测试实战项目(含电商、银行、APP等)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 今天给大家带来几个软件测试项目的实战总结及经验&#xff0c;适…

ps5电玩计时收费系统软件教程,电玩店适合的计时器,电脑定时语音提醒

ps5电玩计时收费系统软件教程&#xff0c;电玩店适合的计时器&#xff0c;电脑定时语音提醒 一、前言 以下软件操作教程以&#xff0c;佳易王电玩计时计费管理软件为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、计时计费功能&#xff1a;只…

PHPStudy 访问网页 403 Forbidden禁止访问

涉及靶场 upload-labd sqli-labs pikachu dvwa 以及所有部署在phpstudy中的靶场 注意&#xff1a;一定要安装解压软件 很多同学解压靶场代码以后访问报错的原因是&#xff1a;电脑上没有解压软件。 这个时候压缩包看起来就是黄色公文包的样子&#xff0c;右键只有“全部提取…

SpringCloud Alibaba Sentinel 修改Dashboard用户名和密码

目录 一、下载Sentinel的Jar包 二、在启动时修改用户名和密码的命令 三、测试登录成功 在网上找到了一大堆文章&#xff0c;没一个有用的&#xff0c;最终还是通过不断测试找到了这个方法。 一、下载Sentinel的Jar包 Releases alibaba/Sentinel GitHub 二、在启动时修改…

设计模式:命令模式

文章目录 一、什么是命令模式二、命令模式结构三、命令模式实现步骤四、命令模式应用场景 一、什么是命令模式 它允许将请求封装为对象&#xff0c;一个请求对应于一个命令&#xff0c;将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作&#xff1a;请求的一方…

【管理咨询宝藏93】大型制造集团数字化转型设计方案

【管理咨询宝藏93】大型制造集团数字化转型设计方案 【格式】PDF版本 【关键词】国际咨询公司、制造型企业转型、数字化转型 【核心观点】 - 235页大型制造型集团数字化转型方案设计&#xff01;细节非常详尽&#xff0c;图表丰富&#xff01; - 系统架构必须采用成熟、具有国…

JS数组操作基础

1、JS数组常用方法 2、函数使用实例 2.1 concat() 功能&#xff1a;可以合并一个或多个数组&#xff0c;返回合并数组之后的数据&#xff0c;不会改变原来的数组 var str1 [12,3,"hello"]; var str2 ["world",123]; console.log(str1,concat(str2)); …

leetcode--560和为k的子数组

问题 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2示例 2&#xff1a; 输入&#xff1a;nums [1,2…
最新文章