实体类的动态生成(三)

前言

在 .NET 中主要有两种动态生成并编译的方式,一种是通过 System.Linq.Expressions 命名空间中的 LambdaExpression 类的 CompileToMethod(...) 方法,但是这种方法只支持动态编译到静态方法,因为这个限制我们只能放弃它而采用 Emitting 生成编译方案,虽然 Emitting 方案强大但是实现起来麻烦不少,必须要手动处理底层 IL 的各种细节,脑补一些 C# 编译器的实现机理,同时还要了解一些基本的 IL(Intermediate Language) 和 CLR(JVM) 执行方面的知识。

基础知识

因为要采用 Emitting 技术方案,必然需要了解 IL,如果你之前没有怎么接触过,也不用灰心,网上有大量关于 IL 的入门文章,“30分钟入门”还是没问题的哈,毕竟 IL 相对 8086/8088 汇编来说,真的平易近人太多了。

首先你需要一个类似 ILSpy(http://ilspy.net) 这样的工具来查看生成的IL或反编译程序集,最近版本还会提供 IL 与对应 C# 的比照解释,用户体验真是体贴得不要不要的。

一、不同于 8086/8088 这样基于寄存器的指令集,IL 和 Java 字节码一样都是基于栈的指令集,它们最明显的区别就是指令的参数指定方式的差异。以“int x = 100+200”操作为例,IL 的指令序列大致是:

ldc.i4 100
ldc.i4 200
add
stlocl.0
  • 前两行代码分别将100和200这两个32位整数加载到运算栈(Evaluation Stack)中;

  • 第3行的 add 是加法运算指令,它会从运算栈弹出(Pop)两次以得到它需要的两个操作数(Operand),计算完成后又会将自己的计算结果压入(Push)到计算栈中,这时栈顶的元素就是累加的结果(即整数300);

  • 第4行的 stloc.0 是设置本地变量的指令,它会从计算栈弹出(Pop)一个元素,然后将该元素保存到特定本地变量中(本示例是第一个本地变量)。注:本地变量必须由方法预先声明。

二、基本上汇编语言或类似 IL 这样的中间指令集都没有高级语言中天经地义的 if/else、switch/case、do/while、for/foreach 这样的基本语言结构,它们只有类似 goto/jump/br 这样的无条件跳转和 br.true/br.false/beq/blt/bgt/ceq/clt/cgt 等之类的条件跳转指令,高级语言中的很多基本语言结构都是由编译器或解释器转换成底层的跳转结构的,所以在 Emitting 中我们也需要脑补编译器中这样的翻译机制,将那些 if/else、while、for 之类的翻译成对应的跳转结构。

需要特别指出的是,因为 C/C++/C#/JAVA 之类的高级语言的逻辑运算中有“短路”的内置约定,所以在转换成跳转结构时,必须留意处理这个问题,否则会破坏语义并可能导致运行时错误。

三、因为 IL 支持类名、字段、属性、方法等元素名称中包含除字母、数字、下划线之外的其他字符,所有各高级语言编译器都会利用该特性,主要是为了避免与特定高级语言中用户代码发生命名冲突,我们亦会采用该策略。

有了上面的基础知识,自己稍微花点时间阅读一些 IL 代码,再来翻阅 Zongsoft.Data.Entity 类的源码就简单了。

另外,在反编译阅读 IL 代码的时候,如果你反编译的是 Debug 版本,会发现生成的 IL 对本地变量的处理非常啰嗦,重复保存又紧接着加载本地变量的操作,这是因为编译器没有做优化导致,不用担心,换成用 Release 编译就好很多了,但是依然还是有一些手动优化的空间。

接口说明

实体动态生成器类的源码位于 Zongsoft.CoreLibrary 项目中(https://github.com/Zongsoft/Zongsoft.CoreLibrary/blob/feature-data/src/Data/Entity.cs),这是一个静态类,其主要公共方法定义如下:

public static Entity
{public static T Build<T>();public static T Build<T>(Action<T> map);public static IEnumerable<T> Build<T>(int count, Action<T, int> map = null);public static object Build(Type type);public static object Build(Type type, Action<object> map);public static IEnumerable Build(Type type, int count, Action<object, int> map = null);
}

公共的 Save() 方法是一个供调试之用的方法,它会将动态编译的程序集保存到文件中,以便使用 ILSpy 这样的工具反编译查看,待 feature-data 合并到 master 分支之后会被移除。

关于跑分

在 https://github.com/Zongsoft/Zongsoft.CoreLibrary/blob/feature-data/samples/Zongsoft.Samples.Entities/Program.cs 类中的 PerformanceDynamic(int count) 是动态生成的跑分(性能测试)代码,需要注意的是,如果是首次动态创建某个实体接口,内部会先进行动态编译。

下面这两种方式跑分测试方式会有不同的性能表现,大家先琢磨下原因再接着往下阅读。

private static void PerformanceDynamic(int count)
{// 获取构建委托,可能会触发内部的预先编译(即预热)var creator = Data.Entity.GetCreator(typeof(Models.IUserEntity));// 创建跑分计时器var stopwatch = new Stopwatch();stopwatch.Start(); //开始计时/* 第一种跑分 */for(int i = 0; i < count; i++){// 调用构建委托来创建实体类实例var user = (Models.IUserEntity)creator();user.UserId = (uint)i;user.Avatar = ":smile:";user.Name = "Name: " + i.ToString();user.FullName = "FullName";user.Namespace = "Zongsoft";user.Status = (byte)(i % byte.MaxValue);user.StatusTimestamp = (i % 11 == 0) ? DateTime.Now : DateTime.MinValue;user.CreatedTime = DateTime.Now;}stopwatch.Restart(); //重新计时/* 第二种跑分 */int index = 0;// 动态构建指定 count 个实体类实例(懒构建)var entities = Data.Entity.Build<Models.IUserEntity>(count);foreach(var user in entities){user.UserId = (uint)index;user.Avatar = ":smile:";user.Name = "Name: " + index.ToString();user.FullName = "FullName";user.Namespace = "Zongsoft";user.Status = (byte)(index % byte.MaxValue);user.StatusTimestamp = (index++ % 11 == 0) ? DateTime.Now : DateTime.MinValue;user.CreatedTime = DateTime.Now;}stopwatch.Stop(); //停止计时
}

在我的老台式机上跑一百万(即count=1,000,000)次,第二种跑分代码比第一种差不多要慢50~100毫秒左右,两者区别就在于 for 循环与 Enumerable/Enumerator 模式的区别,我曾尝试对 Build<T>(int count) 方法内部的 yield return (由C#编译器将该语句翻译成 Enumerable/Enumerator 模式)改为手动实现,优化的思路是:因为在这个场景中,我们已知 count 数量,基于这个必要条件可以剔除 Enumerator 循环中一些不必要的条件判断代码。但是手动写了 Enumerable/Enumerator 后发现,为了代码安全性还是无法省略一些必要的条件判断,因为不能确定用户是否会采用 entities.GetEnumerator() + while 的方式来调用,也就是说即使在确定 count 的条件下也占不到任何性能上的便宜,毕竟基本的代码安全性还是要优先保障的。

如上述所述,动态生成的代码并无性能问题,只是在应对一次性创建上百万个实体实例并遍历的场景下,为了排除 Enumerable/Enumerator 模式对性能的一点点“干扰”(这是必须的)采取了一点优化手段,在实际业务中通常不需这么处理,特此说明。

使用说明

将原有业务系统中各种实体类改为接口,这些接口可以继承自 Zongsoft.Data.IEntity 也可以不用,不管实体接口是否从 Zongsoft.Data.IEntity 接口继承,动态生成的实体类都会实现该接口,因此依然可以将动态创建的实体实例强制转换为该接口。

注意:实体接口中不能含有事件、方法定义,即只能包含属性定义。

变更通知

如果实体需要支持属性变更通知,则实体接口必须增加对 System.ComponentModel.INotifyPropertyChanged 接口的继承,但这样的支持需要付出一点点性能成本,以下是动态生成后的部分C#代码。

public interface IPerson
{string Name { get; set; }
}

// 不支持的属性变更通知版本
public class Person : IPerson, IEntity
{public string Name
   {get => _name;set => {_name = value;_MASK_ |= 1;}}
}

/* 增加对属性变更通知的特性 */
public interface IPerson : INotifyPropertyChanged
{string Name { get; set; }
}

// 支持属性变更通知版本
public class Person : IPerson, IEntity, INotifyPropertyChanged
{// 事件声明public event PropertyChangedEventHandler PropertyChanged;public string Name
   {get => _name;set => {if(_name == value)  // 新旧值比对判断return;_name = value;_MASK_ |= 1;this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));}}
}

所谓一点点性能成本有两点:①需要对新旧值进行比对,比对方法的实现性能对此处有至关影响;②对 PropertyChanged 事件的有效性判断并调用事件委托。当然,如果这是必须的 feature 需求,那就无所谓成本了。

提示:关于新旧值比对的说明,如果属性类型是基元类型,动态生成器会生成 bne/be 这样的特定 IL 指令;否则如果该类型重写了 == 操作符则会使用该操作符的实现;否则会调用 Object.Equals(...) 静态方法来比对。

扩展属性

在某些场景,需要手动处理属性的 getter 或 setter 的业务逻辑,那该如何在动态生成中植入这些逻辑代码呢?在 Zongsoft.Data.Entity 类中有个 PropertyAttribute 自定义特性类,可以利用它来声明扩展属性的实现。譬如下面的示例:

public static UserExtension
{public static string GetAvatarUrl(IUser user){if(string.IsNullOrEmpty(user.Avatar))return null;return "URL:" + user.Avatar;}
}

public interface IUser
{string Avatar { get; set; }[Entity.Property(Entity.PropertyImplementationMode.Extension, typeof(UserExtension))]string AvatarUrl { get; }
}

/*以下的 User 实体类为动态生成器生成的部分示意代码。 */
public class User : IUser, IEntity
{private string _avatar;public string Avatar
   {get => _avatar;set {_avatar = value;_MASK_ |= xxx;}}public string AvatarUrl
   {get {return UserExtension.GetAvatarUrl(this);}}
}

上面的代码比较好理解,就不多说,如果 IUser 接口中的 AvatarUrl 属性是可读写属性或者有 System.ComponentModel.DefaultValueAttribute 自定义特性修饰,那么该属性就会有对应的字段,对应的属性扩展方法也可以获取该字段值。

public static class UserExtension
{public static string GetAvatarUrl(IUser user, string value){if(string.IsNullOrEmpty(value))return $"http://...{user.Avatar}...";return value;}
}

public interface IUser
{string Avatar { get; set; }[Entity.Property(Entity.PropertyImplementationMode.Extension, typeof(UserExtension))]string AvatarUrl { get; set; }
}

/*以下的 User 实体类为动态生成器生成的部分示意代码。 */
public class User : IUser, IEntity
{private string _avatar;private string _avatarUrl;public string Avatar
   {get => _avatar;set {_avatar = value;_MASK_ |= xxx;}}// 只有读取获取扩展方法public string AvatarUrl
   {get => Extension.GetAvatarUrl(this, _avatarUrl);set {_avatarUrl = value;_MASK_ |= xxx;}}
}

当然扩展属性方法支持读写两种,下面是同时实现了两个版本的扩展方法的样子:

public static class UserExtension
{public static string GetAvatarUrl(IUser user, string value){throw new NotImplementedException();}public static bool SetAvatarUrl(IUser user, string value){throw new NotImplementedException();}
}

/*以下的 User 实体类为动态生成器生成的部分示意代码。 */
public class User : IUser, IEntity
{public string AvatarUrl
   {get => UserExtension.GetAvatarUrl(this, _avatarUrl);set {if(UserExtension.SetAvatarUrl(this, _avatarUrl)){_avatarUrl = value;_MASK_ |= xxx;}}}
}

扩展属性方法的定义约定:

  1. 必须是一个公共的静态方法;

  1. 读取方法名以 Get 打头,后面接扩展属性名并区分大小写;

  1. 读取方法的第一个参数必须是要扩展实体接口类型,第二个参数可选,如果有的话必须是扩展属性的类型;返回类型必须是扩展属性的类型;

  1. 设置方法名以 Set 打头,后面接扩展属性名并区分大小写;

  1. 设置方法的第一个参数必须是要扩展实体接口类型,第二参数是扩展属性的类型,表示设置的新值;返回类型必须是布尔类型,返回真(True)表示设置成功否则返回失败(False),只有返回真对应的成员字段才会被设置更新。

单例模式

某些场景中,属性需要采用单例模式来实现,譬如一些集合类型的属性。

public interface IDepartment
{[Entity.Property(Entity.PropertyImplementationMode.Singleton)]ICollection<IUser> Users { get; }
}

/*以下的 Department 实体类为动态生成器生成的部分示意代码。 */
public class Department : IDepartment, IEntity
{private readonly object _users_LOCK;private ICollection<IUser> _users;public Department(){_users_LOCK = new object();}public ICollection<IUser> Users
   {get {if(_users == null) {lock(_users_LOCK) {if(_users == null) {_users = new List<IUser>();}}}return _users;}}
}

实现采用的是双检锁模式,必须注意到,每个单例属性都会额外占用一个用于双检锁的 object 类型变量。

如果属性类型是集合接口,那么动态生成器会选择一个合适的实现该接口的集合类;当然,你也可以自定义一个工厂方法来创建对应的实例,在实体属性中通过 PropertyAttribute 自定特性中声明工厂方法所在的类型即可。

注意:工厂方法必须是一个公共的静态方法,有一个可选的参数,参数类型为实体接口类型。

public static class DepartmentExtension
{public static ICollection<IUser> GetUsers(IDepartment department){return new MyUserCollection(department);}
}

public interface IDepartment
{[Entity.Property(Entity.PropertyImplementationMode.Singleton, typeof(DepartmentExtension))]ICollection<IUser> Users { get; }
}

/*以下的 Department 实体类为动态生成器生成的部分示意代码。 */
public class Department : IDepartment, IEntity
{private readonly object _users_LOCK;private ICollection<IUser> _users;public Department(){_users_LOCK = new object();}public ICollection<IUser> Users
   {get {if(_users == null) {lock(_users_LOCK) {if(_users == null) {_users = DepartmentExtension.GetUsers(this);}}}return _users;}}
}

默认值和自定义初始化

有时我们需要只读属性,但又不需要单例模式这种相对较重的实现机制,可以采用 DefaultValueAttribute 这个自定义特性来处理这种情况。

提示:实体接口或属性声明的所有自定义特性都会被生成器添加到实体类的对应元素中,后面的演示代码可能会省略这些生成的自定义特性,特此说明。

public interface IDepartment
{[DefaultValue("Popeye")]string Name { get; set; }[DefaultValue]ICollection<IUser> Users { get; }
}

/*以下的 Department 实体类为动态生成器生成的部分示意代码。 */
public class Department : IDepartment, IEntity
{private string _name;private ICollection<IUser> _users;public Department(){_name = "Popeye";_users = new List<IUser>();}[DefaultValue("Popeye")]public string Name
   {get => _name;set {_name = value;_MASK_ |= xxx;}}[DefaultValue()]public ICollection<IUser> Users
   {get => _users;}
}

除了支持固定(Mutable)默认值,还支持动态(Immutable)的,所谓动态值是指它的值不在 DefaultValueAttribute 中被固化,即指定 DefaultValueAttribute 的值为一个静态类的类型,该静态类中必须有一个名为 Get 打头并以属性名结尾的方法,该方法可以没有参数,也可以有一个实体接口类型的参数,如下所示。

public static DepartmentExtension
{public static DateTime GetCreationDate(){return DateTime.Now;}
}

public interface IDepartment
{[DefaultValue(typeof(DepartmentExtension))]DateTime CreationDate { get; }
}

/*以下的 Department 实体类为动态生成器生成的部分示意代码。 */
public class Department : IDepartment, IEntity
{private DateTime _creationDate;public Department(){_creationDate = DepartmentExtension.GetCreationDate();}public DateTime CreationDate
   {get => _creationDate;}
}

如果 DefaultValueAttribute 默认值自定义特性中指定的是一个类型(即 System.Type),并且该类型不是一个静态类的类型,并且属性类型也不是 System.Type 的话,那则表示该类型为属性的实际类型,这对于某些属性被声明为接口或基类的情况下尤为有用,如下所示。

public interface IDepartment
{[DefaultValue(typeof(MyManager))]IUser Manager { get; set; }[DefaultValue(typeof(MyUserCollection))]ICollection<IUser> Users { get; }
}

/*以下的 Department 实体类为动态生成器生成的部分示意代码。 */
public class Department : IDepartment, IEntity
{private IUser _manager;private ICollection<IUser> _users;public Department(){_managert = new MyManager();_users = new MyUserCollection();}public IUser Manager
   {get => _manager;set => _manager = value;}public ICollection<IUser> Users
   {get => _users;}
}

其他说明

默认生成的实体属性为公共属性(即非显式实现方式),当出现实体接口在继承中发生了属性重名,或因为某些特殊需求导致必须对某个实体属性以显式方式实现,则可通过 Entity.PropertyAttribute 自定特性中的 IsExplicitImplementation=true 来开启显式实现机制。

在实体接口中声明的各种自定义特性(Attribute),都会被动态生成器原样添加到生成的实体类中。因此之前范例中,凡是接口以及接口的属性声明的各种自定义特性(包括:DefaultValueAttribute 、 Entity.PropertyAttribute )都会被添加到动态生成的实体类的相应元素中,这对于某些应用是一个必须被支持的特性。

性能测试

在《实体类的动态生成(二)》中,我们已经验证过设计方案的执行性能了,但结合上面介绍的功能特性细节,还需再提醒的是:因为开启 DefaultValueAttribute 、扩展属性方法、单例属性、属性变更通知都会导致生成的代码与最基本字段访问方式有所功能增强,对应要跑的代码量增多,因此对跑分是有影响,但这种影响是确定可知的,它们是 feature 所需并非实现方案、算法缺陷所致,敬请知晓。

譬如图二就是增加了属性变更通知(即实体接口继承了 INotifyPropertyChanged)导致的性能影响(Dynamic Entity 所在行)。

640?wx_fmt=png

图一


640?wx_fmt=png

图二

写在最后的话

该实体类动态生成器简单易用、运行性能和内存利用率都非常不错(包括提供 IEntiy 接口的超赞功能),将会成为今后我们所有业务系统的基础结构之一,所以后续的文章中(如果还有的话)应该会经常看到它的应用。

算下来花了整整三天时间(白天晚上都在写)才完成《实体类的动态生成》系列文章,真心觉得写文章比写代码还累,而且这还是省略了应该配有的一些流程图、架构图的情况下。计划接下来我会为 Zongsoft(https://github.com/Zongsoft) 系列开源项目撰写该有的所有文档,照这次这个写法,心底不由升起一丝莫名恐惧和淡淡忧伤来。

如果你觉得这次的文章对你有所帮助,又或者你觉得我们的开源项目做的还不错,请务必为我们点赞并关注我们的公众号,这或许是我坚持写下去的最大动力来源了。

最后,因为写这个东西耽搁了不少造 Zongsoft.Data 这个轮子的时间,所以接下来得全力去造轮子了。打算每周至少一篇干货满满的技术文章在公众号首发,希望不会让自己失望吧。

关于 Zongsoft.Data 它一定会是一款性能满血、易用且足够灵活的数据引擎,首发即会支持四大关系型数据库,后续会加入对 Elasticsearch 的支持,总之,它应该是不同于市面上任何一款 ORM 数据引擎的开源产品。我会陆续与大家分享有关它的一些设计思考以及实现中遇到的问题,当然,也可以在 github 上围观我的进展。

相关文章:

  • 实体类的动态生成(一)

  • 实体类的动态生成(二)

原文地址:http://zongsoft.github.io/blog/zh-cn/zongsoft/entity-dynamic-generation-3/

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

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

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

相关文章

牛客网暑期ACM多校训练营(第三场)

牛客网暑期ACM多校训练营&#xff08;第三场&#xff09; A. PACM Team 01背包&#xff0c;输出方案&#xff0c;用bool存每种状态下用的哪一个物品&#xff0c;卡内存。官方题解上&#xff0c;说用char或者short就行了。还有一种做法是把用的物品压成一个int。 #include <b…

P4096-[HEOI2013]Eden的博弈树

正题 题目链接:https://www.luogu.com.cn/problem/P4096 题目大意 一个博弈树&#xff0c;黑方先手。定义一个最小的叶子节点集为黑胜状态为黑方胜利集合&#xff0c;白色亦然。求所有既属于黑方胜利集合有属于白方胜利集合的点。 解题思路 设fi,0/1f_{i,0/1}fi,0/1​表示ii…

【DP】数字编码

数字编码 题目大意&#xff1a; 有一个序列&#xff0c;可以把它分为一些子序列&#xff0c;但要按题意用字符串表示出来&#xff0c;现在要求字符串的字符最小是多少 原题&#xff1a; 题目描述 一列有顺序的非负整数&#xff0c;需要把它们编码成一个0、10、10、1字符串…

NOIP2013货车运输

NOIP2013货车运输 题目描述 A 国有 n 座城市&#xff0c;编号从 1 到 n&#xff0c;城市之间有 m 条双向道路。每一条道路对车辆都有重量限制&#xff0c;简称限重。现在有 q 辆货车在运输货物&#xff0c;司机们想知道每辆车在不超过车辆限重的情况下&#xff0c;最多能运多重…

52ABP模板 ASP.Net Core 与 Angular的开源实例项目

阅读文本大概需要 5 分钟。开始之前自从上一篇文章".NET:持续进化的统一开发平台"发布后&#xff0c;已经有三个月的时间没有写过文章了。这段时间&#xff0c;做了两场线下活动&#xff0c;一场在上海&#xff0c;一场在成都。 中途顺带去参加了微软的人工智能的ope…

jzoj1281-旅行【dp】

正题 题目大意 nnn个地方&#xff0c;第iii个高度为hih_ihi​。每次可以交换一个hjh_jhj​和hj1h_{j1}hj1​但是要满足操作的jjj递增。 解题思路 也就是可以选择若干个区间&#xff0c;然后将区间的头部丢到尾部。 发现dpdpdp的瓶颈在于我们在枚举下一个时无法知道上一个的具…

初一模拟赛总结(2019.6.15)

成绩&#xff1a; 注&#xff1a;T1好像因为精度问题&#xff0c;有一些本地对的代码交上去WA了 rankrankranknamenamenamescorescorescoreT1T1T1T2T2T2T3T3T3T4T4T4T5T5T5111lyflyflyf320320320202020100100100100100100100100100000222hkyhkyhky298298298989898100100100100…

概率期望学习笔记

概率期望学习笔记 POJ3869 题意&#xff1a;两个人转左轮手枪&#xff0c;朝自己打&#xff0c;枪里保证至少有一个空的&#xff0c;你的对手上一轮活下来了&#xff0c;现在到你了&#xff0c;问重新转左轮和直接打&#xff0c;哪个概率高。 做法&#xff1a;考虑00&#xff0…

jzoj1282-资源勘探【统计】

正题 题目链接:https://gmoj.net/senior/#contest/show/3146/2 题目大意 一个以左上角为端点的子矩形价值定义为区间内唯一的数的数量&#xff0c;求所有子矩形的权值和。 解题思路 考虑每个数字的贡献&#xff0c;对于相同的数字&#xff0c;产生贡献的右下角一定是一个若干…

Quartz.Net分布式任务管理平台(第二版)

前言&#xff1a;在Quartz.Net项目发布第一版Quartz.Net分布式任务管理平台后&#xff0c;有挺多园友去下载使用&#xff0c;我们通过QQ去探讨&#xff0c;其中项目中还是存在一定的不完善。所以有了现在这个版本。这个版本的编写完成其实有段时间了一直没有放上去。现在已经同…

【模拟】表达式求值(jzoj 1768)

表达式求值 jzoj 1768 题目大意&#xff1a; 有一个式子&#xff08;只含数字和加号乘号&#xff09;&#xff0c;让你求出结果的前四位 输入样例 输入样例#1 11*34输入样例#2 11234567890*1 输入样例#3 11000000003*1输出样例 输出样例#1 8输出样例#2 7891输出样例…

Gym101128J

Gym101128J 二分判断点是否在凸包内&#xff0c;模板更新 //Gym - 101128J #include <bits/stdc.h> #define rep(i,a,b) for(int ia;i<b;i) const double eps 1e-8; const double inf 1e20; const double pi acos(-1.0); const int maxp 10110; using namespace s…

.NET Core开发日志——WCF Client

WCF作为.NET Framework3.0就被引入的用于构建面向服务的框架在众多项目中发挥着重大作用。时至今日&#xff0c;虽然已有更新的技术可以替代它&#xff0c;但对于那些既存项目或产品&#xff0c;使用新框架重构的代价未必能找到人愿意买单。而在.NET Core平台环境中&#xff0c…

jzoj4279-[NOIP2015模拟10.29B组]树上路径【树形dp】

正题 题目链接:https://gmoj.net/senior/#main/show/4279 题目大意 nnn个点的一棵树求经过每个点的最长路径。 解题思路 设fif_{i}fi​表示iii子树内的最长路径。 我们第二次转移一个位置时我们枚举除了这个子树之外的其他子树&#xff0c;找到之外最大的fif_ifi​转移下去即…

纪中培训总结(2019年9月4~13日)

Day0&#xff08;4号&#xff09; 今天来到纪中&#xff0c;收拾了一下行李&#xff0c;然后来到机房&#xff0c;老师讲了一下规则&#xff0c;然后刷题去了 Day1&#xff08;5号&#xff09; 早上起来去吃了个早餐&#xff0c;喝了瓶奶&#xff0c;然后来到机房&#xff0…

jzoj4282-[NOIP2015模拟10.29B组]平方数游戏【构造】

正题 题目大意 构造一个ai{1,−1}a_i\{1,-1\}ai​{1,−1}使得最小化∣∑i1naii2∣|\sum_{i1}^na_ii^2|∣i1∑n​ai​i2∣ 解题思路 我们发现有对于一段连续的x2−(x1)2−(x2)2(x3)24x^2-(x1)^2-(x2)^2(x3)^24x2−(x1)2−(x2)2(x3)24&#xff0c;那么就有x2−(x1)2−(x2)2(x3)…

POJ3335(半平面交)

POJ3335 半平面交裸题 //poj3335 #include <cstdio> #include <cmath> #include <algorithm> #define rep(i,a,b) for(int ia;i<b;i) const double eps 1e-8; const double inf 1e20; const double pi acos(-1.0); const int maxp 50110;using namesp…

通过 Docker Compose 组合 ASP NET Core 和 SQL Server

本文模拟一个比较完整的项目&#xff0c;包括前端(MVC), 后端(WebApi)和数据库(mssql-server-linux)。通过Docker Compose 定义&#xff0c;组合并执行它们。涉及到 Docker Compose 安装&#xff0c;命令&#xff0c;docker-compose.yml文件编写&#xff0c;WebApi 和 MVC 项目…

【模拟】交换(jzoj 1518)

交换 jzoj 1518 题目大意&#xff1a; 有两个字符串&#xff08;只包含‘R’‘G’‘B’三个字符&#xff0c;且相邻的字符互不相同&#xff09;&#xff0c;现在要交换两个字符串中的一个数&#xff0c;使两个字符串内都各有3个连续且相同的字符&#xff0c;问有多少种换法 …

组合博弈学习笔记

组合博弈学习笔记 说在前边 下面的博弈题目基本就是sg函数&#xff0c;搜必败必胜态&#xff0c;找规律&#xff0c;推策略。。。没有对理论进行深入了解。HDU1527 搜索时发现&#xff0c;必败态的数对貌似有规律&#xff0c;首先他们的大小两个数的差值是逐个增加的。然后&…