C#的MessagePack(unity)--01

简介

c#中极快的MessagePack序列化器。它比MsgPack-Cli快10倍,并且优于其他c#序列化器。

c#的MessagePack还内置了对LZ4压缩的支持——一种非常快的压缩算法。性能非常重要,特别是在游戏、分布式计算、微服务或数据缓存等应用程序中。

MessagePack具有紧凑的二进制大小和一套完整的通用表达数据类型。

地址:GitHub - MessagePack-CSharp/MessagePack-CSharp: Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). / msgpack.org[C#]

安装

查看地址中README.md,分别有NuGet packages和UnityPack。对于Unity项目,发布页面提供了可下载的.unitypackage文件。当在Unity IL2CPP或Xamarin AOT环境中使用时,请仔细阅读预代码生成部分。

configuration中APILevel改为:.Net Framework

开始

定义要序列化的结构或类,并用[MessagePackObject]属性对其进行注释。用[Key]属性注释值应该序列化的成员(字段和属性)。

using MessagePack;
using UnityEngine;public class Test_MessagePack : MonoBehaviour
{}[MessagePackObject]
public class MyClass
{// Key attributes take a serialization index (or string name)/*键属性接受序列化索引(或字符串名称)*/// The values must be unique and versioning has to be considered as well./*这些值必须是唯一的,并且必须考虑版本控制。*/[Key(0)]public int Age { get; set; }[Key(1)]public string FirstName { get; set; }[Key(2)]public string LastName { get; set; }// 所有不应该序列化的字段或属性必须用[IgnoreMember]注释。[IgnoreMember]public string FullName { get { return FirstName + LastName; } }
}

调用 MessagePackSerializer.Serialize<T>/Deserialize<T> 来序列化/反序列化您的对象实例。您可以使用 ConvertToJson 方法获取任何 MessagePack 二进制数据的可读表示形式。

分析器

MessagePackAnalyzer 包有助于:

1. 自动为可序列化对象定义。
2. 在不正确使用属性、成员可访问性等时生成编译器警告。

内置支持的类型以及自定义自行看官方。

对象序列化

MessagePack for C# 可以序列化您自己的公共类或结构类型。默认情况下,可序列化类型必须使用 [MessagePackObject] 特性进行注释,成员使用 [Key] 特性进行注释。键可以是索引(int)或任意字符串。如果所有键都是索引,则会使用数组进行序列化,这在性能和二进制大小方面具有优势。否则,将使用 MessagePack 映射(字典)。如果您使用 [MessagePackObject(keyAsPropertyName: true)] ,则成员不需要显式的 Key 特性,但是会使用字符串键。

[MessagePackObject]
public class Sample1
{[Key(0)]public int Foo { get; set; }[Key(1)]public int Bar { get; set; }
}[MessagePackObject]
public class Sample2
{[Key("foo")]public int Foo { get; set; }[Key("bar")]public int Bar { get; set; }
}[MessagePackObject(keyAsPropertyName: true)]
public class Sample3
{//不需要 Key 特性。public int Foo { get; set; }//如果要忽略公共成员,可以使用 IgnoreMember 特性。[IgnoreMember]public int Bar { get; set; }
}private void Test_02(){// [10,20]Debug.Log(MessagePackSerializer.SerializeToJson(new Sample1 { Foo = 10, Bar = 20 }));// {"foo":10,"bar":20}Debug.Log(MessagePackSerializer.SerializeToJson(new Sample2 { Foo = 10, Bar = 20 }));// {"Foo":10}Debug.Log(MessagePackSerializer.SerializeToJson(new Sample3 { Foo = 10, Bar = 20 }));}

所有公共实例成员(包括字段和属性)都将被序列化。如果要忽略某些公共成员,请使用 [IgnoreMember] 特性注解该成员。
请注意,任何可序列化的结构或类都必须具有公共访问权限;私有和内部结构和类不能被序列化!要求 MessagePackObject 注释的默认设置是为了强制显式性,因此可能有助于编写更健壮的代码。
您应该使用索引(int)键还是字符串键?我们建议使用索引键以实现更快的序列化和比字符串键更紧凑的二进制表示形式。然而,字符串键中的字符串中的附加信息在调试时非常有用。
当类发生变化或被扩展时,要注意版本控制。MessagePackSerializer 将初始化成员为它们的默认值,如果序列化二进制数据中不存在某个键,则意味着使用引用类型的成员可以初始化为 null。如果您使用索引(int)键,则键应从 0 开始,并且应该是连续的。如果后续版本停止使用某些成员,则应在其他客户端有机会更新并删除对这些成员的使用之前保留这些废弃成员(C# 提供了一个 Obsolete 属性来注解此类成员)。此外,当索引键的值“跳动”很多时,会在序列中留下间隙,这将对二进制大小产生负面影响,因为 null 占位符会被插入到生成的数组中。但是,为了避免客户端之间的兼容性问题或尝试反序列化遗留数据包时出现兼容性问题,不应重用已删除成员的索引。

索引间隙和占位符的例子:

[MessagePackObject]
public class IntKeySample
{[Key(3)]public int A { get; set; }[Key(10)]public int B { get; set; }
}private void Test_03()
{// [null,null,null,0,null,null,null,null,null,null,0]Debug.Log(MessagePackSerializer.SerializeToJson(new IntKeySample()));
}

如果您不希望使用 MessagePackObject/Key 特性进行显式注解,而是希望像 e.g. Json.NET 一样使用 MessagePack for C#,则可以使用无合同解析器。

    private void Test_04(){var data = new ContractlessSample { MyProperty1 = 99, MyProperty2 = 9999 };var bin = MessagePackSerializer.Serialize(data,MessagePack.Resolvers.ContractlessStandardResolver.Options);// {"MyProperty1":99,"MyProperty2":9999}Debug.Log(MessagePackSerializer.ConvertToJson(bin));// 你也可以将ContractlessStandardResolver设置为默认值。// (全局状态;不建议在编写库代码时使用)MessagePackSerializer.DefaultOptions = MessagePack.Resolvers.ContractlessStandardResolver.Options;// Now serializable...var bin2 = MessagePackSerializer.Serialize(data);// {"MyProperty1":99,"MyProperty2":9999}Debug.Log(MessagePackSerializer.ConvertToJson(bin2));}
public class ContractlessSample
{public int MyProperty1 { get; set; }public int MyProperty2 { get; set; }
}

如果您希望序列化私有成员,您可以使用 *AllowPrivate 解析器之一。

如果您希望使用 MessagePack for C# 类似于 BinaryFormatter,具有无类型的序列化 API,则可以使用无类型解析器和助手。请参阅“无类型”部分。 解析器是为 MessagePack for C# 添加自定义类型专用支持的方法。请参考“扩展点”部分。

DataContract compatibility

您可以使用 [DataContract] 标记代替 [MessagePackObject] 标记。如果类型带有 DataContract 标记,您可以使用 [DataMember] 标记代替 [Key] 标记以及 [IgnoreDataMember] 替换 [IgnoreMember] 标记。

然后 [DataMember(Order = int)] 行为与 [Key(int)] 相同,[DataMember(Name = string)] 与 [Key(string)] 相同,而 [DataMember] 则与 [Key(nameof(成员名))] 相同。

使用 DataContract(例如在共享库中)可以使您的类/结构独立于 MessagePack for C# 序列化。但是,它不被分析器支持,也不被 mpc 工具用于代码生成。此外,诸如 UnionAttribute、MessagePackFormatter、SerializationConstructor 等功能也无法使用。因此,我们建议尽可能使用特定的 MessagePack for C# 注释。

MessagePack for C# 支持只读/不可变( readonly/immutable)对象成员的序列化。

MessagePack for C# 支持只读/不可变对象/成员的序列化。例如,这个结构体可以被序列化和反序列化。

[MessagePackObject]
public struct Point
{[Key(0)]public readonly int X;[Key(1)]public readonly int Y;public Point(int x, int y){this.X = x;this.Y = y;}
}private void Test_06(){var data = new Point(99, 9999);var bin = MessagePackSerializer.Serialize(data);//[99,9999]Debug.Log(MessagePackSerializer.ConvertToJson(bin));// 反序列化不可变对象var point = MessagePackSerializer.Deserialize<Point>(bin);//99**9999Debug.Log($"{point.X}**{point.Y}");}

MessagePackSerializer 将选择与索引键匹配的参数列表最好的构造函数,或者使用参数名称作为字符串键。如果找不到合适的构造函数,则会抛出 MessagePackDynamicObjectResolverException:无法找到匹配的构造函数参数异常。可以使用 [SerializationConstructor] 注解手动指定要使用的构造函数。

[MessagePackObject]
public struct Point
{[Key(0)]public readonly int X;[Key(1)]public readonly int Y;[SerializationConstructor]public Point(int x){this.X = x;this.Y = -1;}// 如果没有标记属性,则使用这个(最匹配的参数)。public Point(int x, int y){this.X = x;this.Y = y;}
}

序列化回调

在序列化/反序列化过程中,实现了 IMessagePackSerializationCallbackReceiver 接口的对象将收到 OnBeforeSerialize 和 OnAfterDeserialize 调用。

[MessagePackObject]
public class SampleCallback : IMessagePackSerializationCallbackReceiver
{[Key(0)]public int Key { get; set; }public void OnBeforeSerialize(){Debug.Log("OnBefore");}public void OnAfterDeserialize(){Debug.Log("OnAfter");}
}

联合

MessagePack for C# 支持接口类型和抽象类类型的对象的序列化。它的行为类似于 XmlInclude 或 ProtoInclude。在 MessagePack for C# 中,这些称为联合。只有接口和抽象类才允许标注联合属性。需要唯一的联合密钥。

// 注释继承类型
[MessagePack.Union(0, typeof(FooClass))]
[MessagePack.Union(1, typeof(BarClass))]
public interface IUnionSample { }[MessagePackObject]
public class FooClass : IUnionSample
{[Key(0)]public int XYZ { get; set; }
}[MessagePackObject]
public class BarClass : IUnionSample
{[Key(0)]public string OPQ { get; set; }
}private void Test_08(){//IUnionSample data = new FooClass() { XYZ = 999 };IUnionSample data = new BarClass() { OPQ = "BarClass" };// 序列化接口类型的对象。var bin = MessagePackSerializer.Serialize(data);// 再次进行反序列化。var reData = MessagePackSerializer.Deserialize<IUnionSample>(bin);// 与c# 7.0中的类型切换一起使用switch (reData){case FooClass x:Debug.Log(x.XYZ);break;case BarClass x:Debug.Log(x.OPQ);break;default:break;}}

请注意,在衍生类型中不能重复使用在父类型中已经存在的相同键,因为内部会使用单一的扁平数组或映射,并且因此不能有重复的索引/键。

动态(无类型)反序列化

当调用MessagePackSerializer.Deserialize<object>MessagePackSerializer.Deserialize<dynamic>时,二进制大对象(blob)中的任何值都会被反序列化为其对应的原始类型。如boolcharsbytebyteshortintlongushortuintulongfloatdoubleDateTimestringbyte[]object[]IDictionary<object, object>.

使用字典索引器语法探索对象树是无类型反序列化的最快选项,但它很繁琐且难以阅读和编写。在性能不如代码可读性重要的情况下,可以考虑使用ExpandoObject进行反序列化。

对象类型序列化

StandardResolver 和 ContractlessStandardResolver 是 ServiceStack.Text 库中的两种序列化器,它们能够处理对象类型或匿名类型的序列化。

    private void Test_09(){var objects = new object[] { 1, "aaa", new FooClass { XYZ= 9999 } };var bin = MessagePackSerializer.Serialize(objects);// [1,"aaa",[9999]]Debug.Log(MessagePackSerializer.ConvertToJson(bin));// 支持匿名类型序列化。var anonType = new { Foo = 100, Bar = "foobar" };var bin2 = MessagePackSerializer.Serialize(anonType, MessagePack.Resolvers.ContractlessStandardResolver.Options);// {"Foo":100,"Bar":"foobar"}Debug.Log(MessagePackSerializer.ConvertToJson(bin2));}

在反序列化时, behavior 将与动态(非类型)反序列化相同。

无类型的

无类型API与BinaryFormatter类似,因为它会将类型信息嵌入到blob中,因此在调用API时不需要显式指定类型。

安全

从不受信任的源反序列化数据可能会引入应用程序的安全漏洞。根据反序列化期间使用的设置,不受信任的数据可能能够执行任意代码或导致拒绝服务攻击。不受信任的数据可能来自网络上的不受信任源(例如任何和每个联网客户端),或者在通过未认证连接传输时可以被中间人篡改,或者来自可能已被篡改的本地存储或其他许多来源。MessagePack for C#不提供任何方法来验证数据或使其防篡改。请在反序列化之前使用适当的方法来验证数据 - 例如MAC。

请注意这些攻击情况;过去很多项目、公司以及序列化库用户都曾受到过不受信任的用户数据反序列化的困扰。

当反序列化不受信任的数据时,请将MessagePack配置为更安全的模式,通过配置您的MessagePackSerializerOptions.Security属性:

var options = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData);// Pass the options explicitly for the greatest control.
T object = MessagePackSerializer.Deserialize<T>(data, options);// Or set the security level as the default.
MessagePackSerializer.DefaultOptions = options;

你还应该避免对不受信任的数据使用无类型的序列化程序/格式/解析器,因为这会使不受信任的数据有可能反序列化出未曾预料到的类型,从而破坏安全性。

UntrustedData模式只能抵御一些常见的攻击,但本身并不是一个完全安全的解决方案。

性能

MessagePack For C#使用了许多技术来提高性能。

序列化器使用IBufferWriter<byte>而不是System.IO.Stream来减少内存开销。
缓冲区是从池中租借的,以减少分配,通过减少GC压力保持高吞吐量。
不要创建中间实用工具实例(* Writer / * Reader,* Context等...)
利用动态代码生成和JIT避免值类型的装箱。在禁止JIT的平台上使用AOT生成。
缓存在静态泛型字段上的生成形式(不使用字典缓存,因为字典查找是开销)。参见Resolvers
针对值类型的装箱进行了大量优化的动态IL代码生成和JIT。参见DynamicObjectTypeBuilder。在禁止JIT的平台上使用AOT生成。
当IL代码生成确定目标类型为原始类型时直接调用Primitive API。
减少变量长度格式的分支,当IL代码生成知道目标类型(整数/字符串)范围时
尽可能不要使用IEnumerable<T>抽象迭代集合,参见:CollectionFormatterBase及其派生的集合格式器
使用预生成的查找表来减少mgpack类型约束检查,参见:MessagePackBinary
使用优化的类型键字典用于非泛型方法,参见:ThreadsafeTypeKeyHashTable
避免查找映射(字符串键)的字符串键解码(使用自动机名称查找并与内联IL代码生成一起使用,参见:AutomataDictionary
要编码字符串键,请使用预生成的成员名字节和IL中的固定大小字节数组副本,参见:UnsafeMemory.cs
在这个库之前,我已经实现了快速序列化器与ZeroFormatter# Performance。这是一个进一步进化的实现。MessagePack For C#始终快速且针对所有类型(原始类型、小结构、大对象、任何集合)进行优化。

字符串内联

msgpack格式不允许在数据流中重用字符串。这自然会导致反序列化器为遇到的每一个字符串创建一个新的字符串对象,即使它等于先前遇到的另一个字符串。

当反序列化可能包含重复字符串的数据时,让反序列化器花一点额外的时间检查是否以前见过给定的字符串,并在已见过的情况下重新使用它是值得的。

要在所有字符串值上启用字符串内联,请像这样使用一个在任何标准解析器之前指定StringInterningFormatter的解析器:

    private void Test_10(){var options = MessagePackSerializerOptions.Standard.WithResolver(CompositeResolver.Create(new MessagePack.Formatters.IMessagePackFormatter[] { new MessagePack.Formatters.StringInterningFormatter() },new IFormatterResolver[] { StandardResolver.Instance }));var data = new ClassOfStrings { InternedString= "InternedString",OrdinaryString= "OrdinaryString" };var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);ClassOfStrings newClassOfStrings = MessagePackSerializer.Deserialize<ClassOfStrings>(MessagePackSerializer.Serialize(data, lz4Options), options);Debug.Log($"{newClassOfStrings.InternedString}**{newClassOfStrings.OrdinaryString}");}
[MessagePackObject]
public class ClassOfStrings
{[Key(0)][MessagePackFormatter(typeof(MessagePack.Formatters.StringInterningFormatter))]public string InternedString { get; set; }[Key(1)]public string OrdinaryString { get; set; }
}

如果您正在编写自己的某些含有字符串类型的格式器,则也可以直接从您的格式器中调用StringInterningFormatter。

LZ4压缩

MessagePack是一种快速紧凑的格式,但它不是压缩。LZ4是一个非常快的压缩算法,并且使用它MessagePack for C#可以获得极高的性能以及极其紧凑的二进制大小!

MessagePack for C#内置了LZ4支持。您可以修改options对象并将其传递到这样的API中来激活它:

var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
MessagePackSerializer.Serialize(obj, lz4Options);

MessagePackCompression有两种模式:Lz4Block和Lz4BlockArray。两者都不是简单的二进制LZ4压缩,而是集成到序列化管道中的特殊压缩,使用MessagePack扩展代码(Lz4BlockArray(98)或Lz4Block(99))。因此,它与其他语言提供的压缩兼容性不佳。

Lz4Block将整个MessagePack序列作为单个LZ4块压缩。这是实现最佳压缩比的简单压缩,但在必要时复制整个序列以获取连续内存。

Lz4BlockArray将整个MessagePack序列作为LZ4块数组压缩。压缩/解压块分块,不会进入GC的大对象堆,但是压缩比稍差。

我们建议使用Lz4BlockArray作为默认压缩方式。为了与MessagePack v1.x兼容,请使用Lz4Block。

无论在反序列化时设置了哪种LZ4选项,两种方法都可以反序列化。例如,当使用Lz4BlockArray选项时,可以反序列化使用Lz4Block和Lz4BlockArray的二进制数据。如果未设置压缩选项,则无法解压缩并因此不能进行反序列化。

当使用MessagePack for C#时提高性能的最佳实践

MessagePack for C#默认优先考虑最大性能。然而,也有些选项牺牲性能以换取方便。

1、使用索引键而不是字符串键(Contractless)

不同的选项对于反序列化性能部分显示了索引键(IntKey)和字符串键(StringKey)性能的结果。索引键将以MessagePack数组的形式序列化对象图。字符串键将以MessagePack地图的形式序列化对象图。例如,此类型被序列化为

[MessagePackObject]
public class Person
{[Key(0)] or [Key("name")]public string Name { get; set;}[Key(1)] or [Key("age")]public int Age { get; set;}
}new Person { Name = "foobar", Age = 999 }
  • IntKey["foobar", 999]
  • StringKey{"name:"foobar","age":999}.

IntKey在序列化和反序列化方面总是很快,因为它不必处理和查找键名,并且总能保持较小的二进制大小。

StringKey通常是一个有用的,无合同,简单的JSON替代品,与其他支持MessagePack的语言之间的互操作性和较少的版本错误倾向。但是为了获得最高性能,请使用IntKey。

创建自己的自定义复合解析器

CompositeResolver.Create是一种创建复合解析器的简便方法。但是查找器有一些开销。如果你创建自定义解析器(或使用StaticCompositeResolver.Instance),你可以避免这个开销。

注意:如果你正在创建库,则推荐使用上述自定义解析器而不是CompositeResolver.Create。此外,库不得使用StaticCompositeResolver——因为它是全局状态——以避免兼容性问题。

使用本机解析器

默认情况下,MessagePack for C#将以字符串形式序列化GUID。这比.NET原生格式GUID慢得多。Decimal也是如此。如果你的应用程序大量使用GUID或Decimal并且不必担心与其他语言的互操作性,则可以分别用NativeGuidResolver和NativeDecimalResolver替换它们。

此外,DateTime使用MessagePack时间戳格式进行序列化。通过使用NativeDateTimeResolver,可以保留Kind并更快地进行序列化。

注意复制缓冲区

默认情况下,MessagePackSerializer.Serialize返回byte[]。最终的byte[]是从内部缓冲池复制的。这是额外的成本。您可以使用IBufferWriter<T>或Stream API直接写入缓冲区。如果你想在序列化器之外使用缓冲池,你应该实现自定义的IBufferWriter<byte>或使用Nerdbank.Streams包中的Sequence<T>等现有缓冲池。

在反序列化期间,MessagePackSerializer.Deserialize(ReadOnlyMemory <byte> buffer)优于Deserialize(Stream stream)重载。这是因为Stream API版本首先读取数据、生成ReadOnlySequence <byte>,然后开始反序列化。

选择压缩

如果有重复数据,压缩通常是有效的。在MessagePack中,使用字符串键的对象数组(Contractless)可以有效地进行压缩,因为可以对多个重复的属性名称应用压缩。索引键不如字符串键压缩有效,但是索引键本身就更小。

IntKey(Lz4)压缩效果不佳,但性能仍然有所降低。另一方面,StringKey可以预期对二进制大小产生足够的影响。但是这只是一种示例。根据数据的不同,压缩也可能非常有效,或者除了减慢你的程序外几乎没有其他影响。还存在可以在值中很好地压缩的情况(例如包含许多重复HTML标签的长字符串)。重要的是要逐案核实压缩的实际效果。

扩展

MessagePack for C#提供了用于自定义类型的优化序列化支持的扩展点。官方提供了扩展支持包。

Install-Package MessagePack.ReactiveProperty
Install-Package MessagePack.UnityShims
Install-Package MessagePack.AspNetCoreMvcFormatter

MessagePack.ReactiveProperty包为ReactiveProperty库添加了支持。它增加了ReactiveProperty<>、IReactiveProperty<>、IReadOnlyReactiveProperty<>、ReactiveCollection<>、Unit序列化的支持。这对于保存视图模型状态很有用。

MessagePack.UnityShims包提供了Unity的标准结构(Vector2、Vector3、Vector4、Quaternion、Color、Bounds、Rect、AnimationCurve、Keyframe、Matrix4x4、Gradient、Color32、RectOffset、LayerMask、Vector2Int、Vector3Int、RangeInt、RectInt、BoundsInt)和相应的格式器的支持。它可以启用服务器和Unity客户端之间的正确通信。

    private void Test_11(){// Set extensions to default resolver.var resolver = MessagePack.Resolvers.CompositeResolver.Create(// 将扩展设为默认解析器。ReactivePropertyResolver.Instance,MessagePack.Unity.Extension.UnityBlitResolver.Instance,MessagePack.Unity.UnityResolver.Instance,// 最后使用标准(默认)解析器。StandardResolver.Instance);var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);// 每次传递选项或将之设为默认MessagePackSerializer.DefaultOptions = options;}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法每日一练]-分块(保姆级教程 篇1)POJ3648

插讲一下分块 题目&#xff1a;&#xff08;POJ 3648&#xff09; 一个简单的整数问题 前缀和往往用于静态的不会修改的区间和。遇到经常修改的区间问题&#xff0c;就要用分块或线段树来维护了。 分块算法是优化后的暴力&#xff0c;分块算法有时可以维护一些线段树维护不了的…

【ArcGIS Pro二次开发】:CC工具箱1.1.1更新_免费_安装即可用

CC工具箱1.1.1更新【2023.11.15】 使用环境要求&#xff1a;ArcGIS Pro 3.0 一、下载链接 工具安装文件及使用文档&#xff1a; https://pan.baidu.com/s/1OJmO6IPtMfX_vob3bMtvEg?pwduh5rhttps://pan.baidu.com/s/1OJmO6IPtMfX_vob3bMtvEg?pwduh5r 二、使用方法 1、在下…

数据结构与算法-图

图 &#x1f388;2.图的存储结构&#x1f4d6;2.4.2邻接表的存储✅2.4.2.1逆邻接表✅2.4.2.2邻接表存储结构的定义✅2.4.2.3邻接表存储结构的类定义✅2.4.2.4创建n个顶点m条边的无向网✅2.4.2.5创建n个顶点m条边的有向网✅2.4.2.6定位操作-查找定点信息在顶点数组中的下标✅2.4…

TS的class 继承 类型约束

class修饰符 readonly 只读 private 只能类的内部使用 protected 只能类的内部和继承的子类使用 public不限制 class的super prototype.constructor.call class Doms {name:stringconstructor(name:string) {this.name name}protected getName():string {return this.n…

【UI】饿了么 el-upload如何上传到不同的路径, 根据不同情况上传指不同的接口,不同的路径

在 Element UI 的 Upload 组件中&#xff0c;可以通过在 el-upload 组件中定义 before-upload 回调函数&#xff0c;然后根据上传文件类型等条件在函数中改变 action 属性来实现上传到不同的路径。 template中 <el-upload ref"upload"class"avatar-upload…

【每日一题】689. 三个无重叠子数组的最大和-2023.11.19

题目&#xff1a; 689. 三个无重叠子数组的最大和 给你一个整数数组 nums 和一个整数 k &#xff0c;找出三个长度为 k 、互不重叠、且全部数字和&#xff08;3 * k 项&#xff09;最大的子数组&#xff0c;并返回这三个子数组。 以下标的数组形式返回结果&#xff0c;数组中…

for...of与for...in

for …in for…in循环主要是为遍历对象而设计的&#xff0c;不适用于遍历数组。 for…in循环有几个缺点 1.数组的键名是数字&#xff0c;但是for…in循环是以字符串作为键名“0”、“1”、“2”等等。 2.for…in循环不仅遍历数字键名&#xff0c;还会遍历手动添加的其他键&…

4、FFmpeg命令行操作10

音视频处理流程 先看两条命令 ffmpeg -i test_1920x1080.mp4 -acodec copy -vcodec libx264 -s 1280x720 test_1280x720.flv ffmpeg -i test_1920x1080.mp4 -acodec copy -vcodec libx265 -s 1280x720 test_1280x720.mkv ffmpeg音视频处理流程

【设计原则篇】聊聊里氏替换原则

是什么 子类对象可以替换程序中父类对象出现的任何地方&#xff0c;并且保证原有程序逻辑的正确性不被破坏。 比如我们在实际开发中定义了数据读取的父类&#xff0c;子类可以进行在此功能的拓展、增强但是不能修改原有的内在含义。 里氏替换原则和多态的区别&#xff0c;多态…

拼图小游戏

运行出的游戏界面如下&#xff1a; User类 package domain;/*** ClassName: User* Author: Kox* Data: 2023/2/2* Sketch:*/ public class User {private String username;private String password;public User() {}public User(String username, String password) {this.user…

数据分析—将txt文件转为csv文件;将csv文件转为xls文件

txt文件转为csv文件转化代码&#xff1a; import csv# 输入txt文件路径 txt_file rC:\Users\ZARD\Desktop\daily-min-temperatures.txt# 输出csv文件路径 csv_file rC:\Users\ZARD\Desktop\daily-min-temperatures.csv# 打开txt文件以读取数据 with open(txt_file, r) as tx…

Flink(七)【输出算子(Sink)】

前言 今天是我写博客的第 200 篇&#xff0c;恍惚间两年过去了&#xff0c;现在已经是大三的学长了。仍然记得两年前第一次写博客的时候&#xff0c;当时学的应该是 Java 语言&#xff0c;菜的一批&#xff0c;写了就删&#xff0c;怕被人看到丢脸。当时就想着自己一年之后&…

八:ffmpeg命令提取像素格式和PCM数据

一、提取YUV #提取3秒数据&#xff0c;分辨率和源视频一致 fmpeg -i test_1280x720.mp4 -t 3 -pix_fmt yuv420p yuv420p_orig.yuv#提取3秒数据&#xff0c;分辨率转为320x240 ffmpeg -i test_1280x720.mp4 -t 3 -pix_fmt yuv420p -s 320x240 yuv420p_320x240.yuv 二、提取RGB…

Windows上搭建一个网站(基本生产环境)

前言 本博客记录的是Windows上一次网站搭建的过程&#xff0c;主要是在前端采用的是React&#xff0c;后端采用的是Flask&#xff0c;记录一下生产版本搭建流程和坑点&#xff0c;供有缘人一起进步&#xff0c;当然本博客还存在很多不足。 前端项目构建生产版本 以React为例…

将AI技术与VR元宇宙相结合的整体解决方案

当前人工智能与VR虚拟现实两大热门技术的融合&#xff0c;正引领着人类走向更智能、更数字化、更便捷、更快速的时代。将这两者结合&#xff0c;AI智能检索应用到VR教学中&#xff0c;将为教育带来前所未有的好处。 个性化教学体验 通过AI智能检索&#xff0c;VR教学可以针对每…

Pandas+Matplotlib 数据分析

利用可视化探索图表 一、数据可视化与探索图 数据可视化是指用图形或表格的方式来呈现数据。图表能够清楚地呈现数据性质&#xff0c; 以及数据间或属性间的关系&#xff0c;可以轻易地让人看图释义。用户通过探索图&#xff08;Exploratory Graph&#xff09;可以了解数据的…

实在智能携手中国电信翼支付,全球首款Agent智能体亮相2023数字科技生态大会

11月10日-13日&#xff0c;中国电信与广东省人民政府联合主办的“2023数字科技生态大会”在广州隆重举行。本届大会以“数字科技焕新启航”为主题&#xff0c;邀请众多生态合作伙伴全方位展示数字科技新成果&#xff0c;包括数字新消费、产业数字化、智能电子、人工智能大模型等…

em/px/rem/vh/vw 的区别?

px px表示像素&#xff0c;所谓像素就是呈现在显示器上的一个个小点&#xff0c;每个像素点都是大小等同的&#xff0c;所以像素为绝对长度单位。 在移动端中存在设备像素比&#xff0c;px实际显示的大小是不确定&#xff0c;之所以认为px为绝对单位&#xff0c;在于px的大小和…

K-Means算法进行分类

已知数据集D中有9个数据点&#xff0c;分别是&#xff08;1,2&#xff09;&#xff0c;(2,3), (2,1), (3,1),(2,4),(3,5),(4,3),(1,5),(4,2)。采用K-Means算法进行聚类&#xff0c;k2&#xff0c;设初始中心点为&#xff08;1.1,2.2&#xff09;&#xff0c;&#xff08;2.3,3.…

OpenAI发布会中不起眼的重大更新

上周&#xff0c;OpenAI的历史首届开发者大会上&#xff0c;OpenAI的首席执行官山姆奥特曼展示了一系列产品更新&#xff0c;包含了众多重磅功能&#xff0c;就算单独拿出来都能让科技圈震一震&#xff0c;一下能发布这么多也真是家底厚。 果不其然&#xff0c;接下来的一周&am…