layer.js源码分析 - 玉案轩窗的博客 - CSDN博客

mikel阅读(1075)

来源: layer.js源码分析 – 玉案轩窗的博客 – CSDN博客

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/s1879046/article/details/76034784
最近在看layer.js源码,从中得到了一些启发,对于一个框架的设计也有了一定的看法,现在对于这个框架的设计以及其他的问题来说明一下。

layer.js是一个专注于弹出层的框架,这个框架本身可以实现5种弹出层类型,其他的就不多说了,可以去看看它的官网,下面说一下它的主要组织形式:

首先,这个框架本身就是一个IIFE(立即执行函数表达式),保证了局部环境,避免了全局变量污染的问题
框架内部主要是三个对象构成,分别是Class构造函数、layer对象、ready对象
通过window来暴露对外api
以前看过一点JQuery的源码,layer.js的框架结构和JQuery是相同形式,框架内部主要是这三个对象来组成,对于这三个对象上具体的方法以及属性我列举了下,如下图所示:

整个框架的结构组织以及脉络还是很清晰的,框架整体的代码量大概1300多行左右,我对这个框架运行的具体流程做了个较为详细的流程,具体流程如下:

上面图片中是该框架初始化过程所做的工作,具体框架代码的详细注释在我的Github上,如果你感兴趣,可以看看。
致远行的你我
————————————————
版权声明:本文为CSDN博主「玉案轩窗」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s1879046/article/details/76034784

layui框架详细分析系列之熟悉框架以及提供的页面元素 - 玉案轩窗的博客 - CSDN博客

mikel阅读(839)

来源: layui框架详细分析系列之熟悉框架以及提供的页面元素 – 玉案轩窗的博客 – CSDN博客

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/s1879046/article/details/76095467
前不久,阅读了layer.js的源码,实际上它是layui框架的内置模块,不过可以独立出去使用,主要是用于弹出层的,layui框架中内置了很多模块,在未来的一段时间内,我都会对于这个框架的各个内置模块代码以及整个逻辑进行详细的描述和说明,以此来提高自己的各个方面的能力。

layui是一个UI框架,它的官网对其的说明是经典模块化前端框架,该框架的内部提供了一些UI框架常有的功能点,例如按钮、表单等。

先说说为什么选择这个框架作为学习源码过程中的第一步,原因大概有如下几点:

该框架不依赖与其他的Js框架,虽然它有的模块需要JQuery
它是基于原生Js的,对于我来说我不需要去了解其他Js的框架或库,减少分析该框架的成本
就正式进入正题吧,看框架源码是要有目的性的,这句话确实不错啊。如果只是为看而看,当你看到一定程度后,你就不想看了,代码是枯燥的,特别是看别人的源码,有了目的性,就有所不同,你知道自己想要什么。
在正式进入框架代码阅读分析之前,我认为需要熟悉和使用该框架,这样你在分析其源码的过程中,会更加清晰具体的功能。下面主要介绍该框架中命名规范以及提供的页面元素。
该框架中关于css类的命名规范分为两种,一种是具体模块的css的命令,它遵循layui-模块-状态或类型,另一种是公共类(可以说是不具体属于哪一个模块的),它遵循layui-状态或类型。
该框架中关于js的命名规则:
变量基本采用小写
方法名采用驼峰法命名
逻辑相关的都以is开头
获取相关的方法是以get开头
文件名小写
该框架提供的页面元素有:
内置几种背景颜色,主要的css类:.layui-bg-red、.layui-bd-orange等
图标,使用的是iconfont
按钮以及按钮组,分为大小、状态等,主要的css类:.layui-btn、.layui-btn-primary、.layui-btn-big等
表单,主要的css类有:.layui-form等
导航,分为水平导航、垂直导航、侧边栏导航,主要css类有:.layui-nav、.layui-nav-tree、.layui-nav-side等
选项卡,分为卡片选项卡、简洁风格、可删除的,主要css类有:.layui-tab等
表格,主要css类有.layui-table等
进度条,主要css类有.layui-progress、.layui-progress-bar等
面板,手风琴风格,主要的css类有.layui-collapse等
上面提供的都是css类,用于呈现该框架UI风格,一些功能需要交互才可以完成,所以需要引入模块来保证完整功能的使用。
本文主要是描述下该框架的css命名规范以及熟悉该框架,不出意外,明天会分析该框架中自己提供的模块加载机制,该收拾下班了。
致远行的你我
————————————————
版权声明:本文为CSDN博主「玉案轩窗」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s1879046/article/details/76095467

layui框架详细分析系列之框架主体组织结构 - 玉案轩窗的博客 - CSDN博客

mikel阅读(821)

来源: layui框架详细分析系列之框架主体组织结构 – 玉案轩窗的博客 – CSDN博客

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/s1879046/article/details/76162161
layui框架主体
今天正式的进入框架主体部分的学习与分析,该框架开源从GitHub上clone下来的源码主要的部分就是src部分,该部分主要的目录结构构成如下:

从上图可以看出css存储样式,font存储图标(iconfont), images存储图片,lay存储其他内置模块的js文件,layui.js文件就是主体部分。
layui.js还是采用IIFE的形式构成,如下面所示:

;!function(window, undefined) {
// 主要代码
}(window);
1
2
3
其中;号与!号的作用就不在啰嗦了,我之前的文章有对其进行解释,我分析了layui.js整体,绘制了思维导图,具体如下图所示:

从上图可以看出,主体文件的组织结构很清晰明了,主要分为:
1、需要用到的变量,比如内置的模块对象、获取layui.js文件路径的函数以及非常重要的config配置参数对象
2、Lay构造函数,面向对象编程,方法都定义在构造函数的原型对象上
3、通过Lay构造函数创建对象,并通过window对象将其暴露出去

主体结构清晰简洁,封装性感觉很好,没有不必要的属性暴露出去,框架提供的页面元素的相关都是通过定义的CSS类来实现,行为和样式分离开来,耦合性相对较小。

该框架实现了自己的模块加载机制以及自定义事件机制,实现方法也是定义在Lay的原型对象上,我是通过分析内置模块来分析相关联的主体函数的功能的,理解并给予详细的注释。

本篇文章主要分析主体文件的组织结构,相关注释的主体文件以及各个内置模块的注释源文件都会上传到我的Github上,下一篇将会分析该框架的模块加载机制。
远行的你我
————————————————
版权声明:本文为CSDN博主「玉案轩窗」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s1879046/article/details/76162161

layui源码详细分析系列之element模块以及自定义事件机制 - 玉案轩窗的博客 - CSDN博客

mikel阅读(803)

来源: layui源码详细分析系列之element模块以及自定义事件机制 – 玉案轩窗的博客 – CSDN博客

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/s1879046/article/details/76216485
element内置模块时layui框架中页面元素的交互功能的实现,主要是选项卡、面板、导航等的交互的实现。

下面先分析element模块的组织结构,具体如下图所示:

从上图中可以看出,element模块使用该框架自己的模块加载机制来加载该模块。

该框架内置的模块都是采用面向对象的编程,该模块也是,定义了Element构造函数,对外的API接口都定义在Element的原型对象上,使用JQuery来绑定一些原生的事件。call对象中定义真正处理选项卡等交互以及初始状态的处理程序。

element模块中使用框架自己定义的事件处理机制,具体实际上使用layui主体文件中的onevent和event来进行事件的注册以及事件的执行。

因为该模块最后输出的是函数类型,所以在使用该模块时要求如下:

layui.use([‘element’], function() {
var element = layui.element();
});
1
2
3
上面实际会自动执行一些初始化的工作,具体就会选项卡、面板等的初始状态的设置。

下面讲解layui自定义的事件机制,该框架定义事件名的形式如下:

功能名(lay-filter属性名)
1
在该框架中有lay-filter属性,该属性就是用于事件标识的。

什么时候使用该框架内置的事件机制?当你想要执行其他的操作,例如获取相关数据等,就可以使用自定义的事件机制。

下面使用实例来讲解框架事件机制的具体的逻辑处理,假设选项卡有属性lay-filter=’demo’, 那么就可以使用该框架自定义的事件机制,具体如下:

使用该机制的代码:
layui.use([‘element’], function() {
var ele = layui.element();

// tab(demo)就是事件名
ele.on(‘tab(demo)’, function(data) {
cosnole.log(data);
});
});
1
2
3
4
5
6
7
8
9
具体的逻辑流程如下图:

具体如上图所示,实际上内部维持了config.event对象来保存事件,onevent实现事件注册以及监听,实际上就是存储在config.event对象中,具体存储形式如下:

config.event[modeName + ‘.’ + event] = [callback]
1
在本例中modeName为element,event为tab(demo), callback就是就是事件处理程序,本例中callback如下:

callback = functio(data) {
console.log(data);
};
1
2
3
详细的代码注释以及相关的逻辑图我会上传到我的Github上,通过今天对于现在这种代码学习,有些迷茫了。

总结下现在这种阅读代码的方式的缺点:

有点纸上谈兵的感觉,不充实很虚
有些为看而看的感觉,没有非常大的收获,反而有些疲惫,没有自己成长了的自豪感
所以以后的文章就不会是这种方式出现了,目前想到的很好提高自己同时最大程度的提高自己,以后的风格将会以事实为依据,来具体展开,具体的形式如下:

自己会动手编写一个简易版实现核心功能的demo,并给出自己的思路
查看框架作者实现的思路,比较自己的思路与作者思路的相同点以及不同点,进行分析总结,丰满自己的思路
今天就到此为止,走走停停,方能走得更远,明天会分析文件上传以及流加载模块。
致远行的你我
————————————————
版权声明:本文为CSDN博主「玉案轩窗」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s1879046/article/details/76216485

git将本地项目托管到码云上(含git更换远程仓库步骤) - zhangzeshan的博客 - CSDN博客

mikel阅读(852)

来源: git将本地项目托管到码云上(含git更换远程仓库步骤) – zhangzeshan的博客 – CSDN博客

首先在码云上创建一个空项目

然后将自己本地的项目换成一个仓库,执行如下命令:(cd到自己的项目下)

git init
执行完毕后,本地项目就会生成.git的文件

下一步,添加远程仓库,也就是在码云上创建的空项目的链接

git remote add origin 码云项目的链接
下一步,将码云上的仓库pull到本地文件夹

git pull origin master
下一步,将自己的改动的数据进行提交:

git add .
然后添加提交的详细描述

git commit -m ‘新添加的文件内容描述’
然后推送到远程仓库:

git push origin master
如果这一步执行后,出现错误

那就执行一句命令:

git push -u origin master -f
就能完成托管了

 

下面说下更换代码仓库地址的具体步骤:

如果我刚刚添加的远程仓库不要了,需要更换到其他的仓库去,

首先,先把自己的原来的远程仓库删除:

git remote rm origin
然后,添加现在新的远程仓库地址

git remote add origin 新仓库地址
接下里,进行提交,测试看看自己是否更改成功:

git add .
git commit -m”#zhangzeshan#up”
git pull origin master
git push origin master
按照步骤,执行后完成后,就可以到码云上是否有自己提交的记录了

如果哪里说错,欢迎指出!
————————————————
版权声明:本文为CSDN博主「张芝山」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhangzeshan/article/details/82986434

VS2017中使用码云上传项目以及问题汇总 - 马儿不吃草 - CSDN博客

mikel阅读(1078)

来源: VS2017中使用码云上传项目以及问题汇总 – 马儿不吃草 – CSDN博客

方法一:个人项目和企业项目都可用此方法上传项目
1.下载码云在VS中的插件:下载地址

2.下载之后重启VS,然后打开团队资源管理器,也可以点击“团队”—>“管理连接”,在如下界面中点击连接,输入码云的账号密码

3.在码云的网站中进行新建项目(如果是公司项目归属选择公司即可)

4.项目新建完后在vs中点击克隆,选择刚刚新建的项目,然后选择项目的本地保存地址,点击克隆,克隆以后本地GIT存储库也会复制一份

5.然后将做好的项目拷贝到刚刚新建的目录下,如果是新项目,直接在该目录下进行新建项目即可

6.在本地GIT存储库中双击该项目,跳到该界面

7.如果是新项目第一次上传,点击更改,然后填写信息,点击全部提交并同步即可。在网站上就可以看到自己上传的项目啦,如果点击的只是全部提交,那么在提交后需要在同步中点一下同步按钮

8.如果是修改后项目要上传同步,点击更改,然后点击操作,修改上一个提交,然后再点击同步。一定要点击同步,不同步的话代码不会上传到云上面

方法二:上传个人项目比较推荐,不建议企业项目使用该方式上传
1.在VS中的Gitee中点击创建仓库,选择好必要的信息和路径,点击新建

2.然后和方法一一样将项目源码拷贝到刚才新建的目录中,之后的操作就和方法一一样了

利用这种方式的话,项目默认是为开源的,转移到企业里需要企业转移码。

 

注意点:
1.上传的单个文件不能大于100M

2.不要上传bin目录和obj目录下的文件,上传时将需要上传的文件加到暂存中,然后再点操作,提交上一个修改,同步,暂存中的文件就会上传上去,更改数中的文件就不会上传
————————————————
版权声明:本文为CSDN博主「马儿不吃草」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/horseroll/article/details/82499556

Newtonsoft.Json高级用法 - 焰尾迭 - 博客园

mikel阅读(894)

来源: Newtonsoft.Json高级用法 – 焰尾迭 – 博客园

手机端应用讲究速度快,体验好。刚好手头上的一个项目服务端接口有性能问题,需要进行优化。在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验。本篇将为大家介绍Newtonsoft.Json的一些高级用法,可以修改很少的代码解决上述问题。

阅读目录

Newtonsoft.Json介绍

在做开发的时候,很多数据交换都是以json格式传输的。而使用Json的时候,我们很多时候会涉及到几个序列化对象的使用:DataContractJsonSerializer,JavaScriptSerializer  Json.NET即Newtonsoft.Json。大多数人都会选择性能以及通用性较好Json.NET,这个不是微软的类库,但是一个开源的世界级的Json操作类库,从下面的性能对比就可以看到它的其中之一的性能优点。

齐全的API介绍,使用方式简单

 

基本用法

Json.Net是支持序列化和反序列化DataTable,DataSet,Entity Framework和Entity的。下面分别举例说明序列化和反序列化。

DataTable:

复制代码
            //序列化DataTable
            DataTable dt = new DataTable();
            dt.Columns.Add("Age", Type.GetType("System.Int32"));
            dt.Columns.Add("Name", Type.GetType("System.String"));
            dt.Columns.Add("Sex", Type.GetType("System.String"));
            dt.Columns.Add("IsMarry", Type.GetType("System.Boolean"));
            for (int i = 0; i < 4; i++)
            {
                DataRow dr = dt.NewRow();
                dr["Age"] = i + 1;
                dr["Name"] = "Name" + i;
                dr["Sex"] = i % 2 == 0 ? "男" : "女";
                dr["IsMarry"] = i % 2 > 0 ? true : false;
                dt.Rows.Add(dr);
            }
            Console.WriteLine(JsonConvert.SerializeObject(dt));
复制代码

利用上面字符串进行反序列化

 string json = JsonConvert.SerializeObject(dt);
 dt=JsonConvert.DeserializeObject<DataTable>(json);
 foreach (DataRow dr in dt.Rows)
 {
   Console.WriteLine("{0}\t{1}\t{2}\t{3}\t", dr[0], dr[1], dr[2], dr[3]);
 }

Entity序列化和DataTable一样,就不过多介绍了。

高级用法

1.忽略某些属性

2.默认值的处理

3.空值的处理

4.支持非公共成员

5.日期处理

6.自定义序列化的字段名称

7.动态决定属性是否序列化

8.枚举值的自定义格式化问题

9.自定义类型转换

10.全局序列化设置

 一.忽略某些属性

类似本问开头介绍的接口优化,实体中有些属性不需要序列化返回,可以使用该特性。首先介绍Json.Net序列化的模式:OptOut 和 OptIn

OptOut 默认值,类中所有公有成员会被序列化,如果不想被序列化,可以用特性JsonIgnore
OptIn 默认情况下,所有的成员不会被序列化,类中的成员只有标有特性JsonProperty的才会被序列化,当类的成员很多,但客户端仅仅需要一部分数据时,很有用

 

 

仅需要姓名属性

复制代码
    [JsonObject(MemberSerialization.OptIn)]
    public class Person
    {
        public int Age { get; set; }

        [JsonProperty]
        public string Name { get; set; }

        public string Sex { get; set; }

        public bool IsMarry { get; set; }

        public DateTime Birthday { get; set; }
    }
复制代码

不需要是否结婚属性

复制代码
    [JsonObject(MemberSerialization.OptOut)]
    public class Person
    {
        public int Age { get; set; }

        public string Name { get; set; }

        public string Sex { get; set; }

        [JsonIgnore]
        public bool IsMarry { get; set; }

        public DateTime Birthday { get; set; }
    }
复制代码

通过上面的例子可以看到,要实现不返回某些属性的需求很简单。1.在实体类上加上[JsonObject(MemberSerialization.OptOut)] 2.在不需要返回的属性上加上 [JsonIgnore]说明。

二.默认值处理

序列化时想忽略默认值属性可以通过JsonSerializerSettings.DefaultValueHandling来确定,该值为枚举值

DefaultValueHandling.Ignore
序列化和反序列化时,忽略默认值
DefaultValueHandling.Include
序列化和反序列化时,包含默认值

 

 

 

 [DefaultValue(10)]
 public int Age { get; set; }
 Person p = new Person { Age = 10, Name = "张三丰", Sex = "男", IsMarry = false, Birthday = new DateTime(1991, 1, 2) };
 JsonSerializerSettings jsetting=new JsonSerializerSettings();
 jsetting.DefaultValueHandling=DefaultValueHandling.Ignore;
 Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

最终结果如下:

 

三.空值的处理

  序列化时需要忽略值为NULL的属性,可以通过JsonSerializerSettings.NullValueHandling来确定,另外通过JsonSerializerSettings设置属性是对序列化过程中所有属性生效的,想单独对某一个属性生效可以使用JsonProperty,下面将分别展示两个方式

1.JsonSerializerSettings

 Person p = new Person { room=null,Age = 10, Name = "张三丰", Sex = "男", IsMarry = false, Birthday = new DateTime(1991, 1, 2) };
 JsonSerializerSettings jsetting=new JsonSerializerSettings();
 jsetting.NullValueHandling = NullValueHandling.Ignore;
 Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

2.JsonProperty

通过JsonProperty属性设置的方法,可以实现某一属性特别处理的需求,如默认值处理,空值处理,自定义属性名处理,格式化处理。上面空值处理实现

 [JsonProperty(NullValueHandling=NullValueHandling.Ignore)]
 public Room room { get; set; }

 

四.支持非公共成员

序列化时默认都是处理公共成员,如果需要处理非公共成员,就要在该成员上加特性”JsonProperty”

 [JsonProperty]
 private int Height { get; set; }

 

五.日期处理

  对于Dateime类型日期的格式化就比较麻烦了,系统自带的会格式化成iso日期标准,但是实际使用过程中大多数使用的可能是yyyy-MM-dd 或者yyyy-MM-dd HH:mm:ss两种格式的日期,解决办法是可以将DateTime类型改成string类型自己格式化好,然后在序列化。如果不想修改代码,可以采用下面方案实现。

Json.Net提供了IsoDateTimeConverter日期转换这个类,可以通过JsnConverter实现相应的日期转换

    [JsonConverter(typeof(IsoDateTimeConverter))]
    public DateTime Birthday { get; set; }

但是IsoDateTimeConverter日期格式不是我们想要的,我们可以继承该类实现自己的日期

复制代码
    public class ChinaDateTimeConverter : DateTimeConverterBase
    {
        private static IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" };

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return dtConverter.ReadJson(reader, objectType, existingValue, serializer);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            dtConverter.WriteJson(writer, value, serializer);
        }
    }
复制代码

自己实现了一个yyyy-MM-dd格式化转换类,可以看到只是初始化IsoDateTimeConverter时给的日期格式为yyyy-MM-dd即可,下面看下效果

[JsonConverter(typeof(ChinaDateTimeConverter))]
public DateTime Birthday { get; set; }

可以根据自己需求实现不同的转换类

 

六.自定义序列化的字段名称

    实体中定义的属性名可能不是自己想要的名称,但是又不能更改实体定义,这个时候可以自定义序列化字段名称。

     [JsonProperty(PropertyName = "CName")]
     public string Name { get; set; }

 

七.动态决定属性是否序列化

  这个是为了实现@米粒儿提的需求特别增加的,根据某些场景,可能A场景输出A,B,C三个属性,B场景输出E,F属性。虽然实际中不一定存在这种需求,但是json.net依然可以支持该特性。

继承默认的DefaultContractResolver类,传入需要输出的属性

重写修改了一下,大多数情况下应该是要排除的字段少于要保留的字段,  为了方便书写这里修改了构造函数加入retain表示props是需要保留的字段还是要排除的字段

复制代码
public class LimitPropsContractResolver : DefaultContractResolver
    {
        string[] props = null;

        bool retain;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="props">传入的属性数组</param>
        /// <param name="retain">true:表示props是需要保留的字段  false:表示props是要排除的字段</param>
        public LimitPropsContractResolver(string[] props, bool retain=true)
        {
            //指定要序列化属性的清单
            this.props = props;

            this.retain = retain;
        }

        protected override IList<JsonProperty> CreateProperties(Type type,

        MemberSerialization memberSerialization)
        {
            IList<JsonProperty> list =
            base.CreateProperties(type, memberSerialization);
            //只保留清单有列出的属性
            return list.Where(p => {
                if (retain)
                {
                    return props.Contains(p.PropertyName);
                }
                else
                {
                    return !props.Contains(p.PropertyName);
                }      
            }).ToList();
        }
复制代码
        public int Age { get; set; }

        [JsonIgnore]
        public bool IsMarry { get; set; }

        public string Sex { get; set; }
      JsonSerializerSettings jsetting=new JsonSerializerSettings();
      jsetting.ContractResolver = new LimitPropsContractResolver(new string[] { "Age", "IsMarry" });
      Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

使用自定义的解析类,只输出”Age”, “IsMarry”两个属性,看下最终结果.只输出了Age属性,为什么IsMarry属性没有输出呢,因为标注了JsonIgnore

看到上面的结果想要实现pc端序列化一部分,手机端序列化另一部分就很简单了吧,我们改下代码实现一下

复制代码
  string[] propNames = null;
  if (p.Age > 10)
  {
    propNames = new string[] { "Age", "IsMarry" };
  }
  else
  {
      propNames = new string[] { "Age", "Sex" };
  }
  jsetting.ContractResolver = new LimitPropsContractResolver(propNames);
  Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));
复制代码

 

八.枚举值的自定义格式化问题

  默认情况下对于实体里面的枚举类型系统是格式化成改枚举对应的整型数值,那如果需要格式化成枚举对应的字符怎么处理呢?Newtonsoft.Json也帮我们想到了这点,下面看实例

复制代码
    public enum NotifyType
    {
        /// <summary>
        /// Emil发送
        /// </summary>
        Mail=0,

        /// <summary>
        /// 短信发送
        /// </summary>
        SMS=1
    }

    public class TestEnmu
    {
        /// <summary>
        /// 消息发送类型
        /// </summary>
        public NotifyType Type { get; set; }
    }
    JsonConvert.SerializeObject(new TestEnmu());
复制代码

输出结果:  现在改造一下,输出”Type”:”Mail”

复制代码
    public class TestEnmu
    {
        /// <summary>
        /// 消息发送类型
        /// </summary>
        [JsonConverter(typeof(StringEnumConverter))]
        public NotifyType Type { get; set; }
    }
复制代码

其它的都不变,在Type属性上加上了JsonConverter(typeof(StringEnumConverter))表示将枚举值转换成对应的字符串,而StringEnumConverter是Newtonsoft.Json内置的转换类型,最终输出结果

 

九.自定义类型转换

默认情况下对于实体里面的Boolean系统是格式化成true或者false,对于true转成”是” false转成”否”这种需求改怎么实现了?我们可以自定义类型转换实现该需求,下面看实例

复制代码
public class BoolConvert : JsonConverter
    {
        private string[] arrBString { get; set; }

        public BoolConvert()
        {
            arrBString = "是,否".Split(',');
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="BooleanString">将bool值转换成的字符串值</param>
        public BoolConvert(string BooleanString)
        {
            if (string.IsNullOrEmpty(BooleanString))
            {
                throw new ArgumentNullException();
            }
            arrBString = BooleanString.Split(',');
            if (arrBString.Length != 2)
            {
                throw new ArgumentException("BooleanString格式不符合规定");
            }
        }


        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            bool isNullable = IsNullableType(objectType);
            Type t = isNullable ? Nullable.GetUnderlyingType(objectType) : objectType;

            if (reader.TokenType == JsonToken.Null)
            {
                if (!IsNullableType(objectType))
                {
                    throw new Exception(string.Format("不能转换null value to {0}.", objectType));
                }

                return null;
            }

            try
            {
                if (reader.TokenType == JsonToken.String)
                {
                    string boolText = reader.Value.ToString();
                    if (boolText.Equals(arrBString[0], StringComparison.OrdinalIgnoreCase))
                    {
                        return true;
                    }
                    else if (boolText.Equals(arrBString[1], StringComparison.OrdinalIgnoreCase))
                    {
                        return false;
                    }
                }

                if (reader.TokenType == JsonToken.Integer)
                {
                    //数值
                    return Convert.ToInt32(reader.Value) == 1;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Error converting value {0} to type '{1}'", reader.Value, objectType));
            }
            throw new Exception(string.Format("Unexpected token {0} when parsing enum", reader.TokenType));
        }

        /// <summary>
        /// 判断是否为Bool类型
        /// </summary>
        /// <param name="objectType">类型</param>
        /// <returns>为bool类型则可以进行转换</returns>
        public override bool CanConvert(Type objectType)
        {
            return true;
        }


        public bool IsNullableType(Type t)
        {
            if (t == null)
            {
                throw new ArgumentNullException("t");
            }
            return (t.BaseType.FullName=="System.ValueType" && t.GetGenericTypeDefinition() == typeof(Nullable<>));
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null)
            {
                writer.WriteNull();
                return;
            }

            bool bValue = (bool)value;

            if (bValue)
            {
                writer.WriteValue(arrBString[0]);
            }
            else
            {
                writer.WriteValue(arrBString[1]);
            }
        }
    }
复制代码

自定义了BoolConvert类型,继承自JsonConverter。构造函数参数BooleanString可以让我们自定义将true false转换成相应字符串。下面看实体里面怎么使用这个自定义转换类型

    public class Person
    {
        [JsonConverter(typeof(BoolConvert))]
        public bool IsMarry { get; set; }
    }

相应的有什么个性化的转换需求,都可以使用自定义转换类型的方式实现。

 

十.全局序列化设置

文章开头提出了Null值字段怎么不返回的问题,相应的在高级用法也给出了相应的解决方案使用jsetting.NullValueHandling = NullValueHandling.Ignore; 来设置不返回空值。这样有个麻烦的地方,每个不想返回空值的序列化都需设置一下。可以对序列化设置一些默认值方式么?下面将解答

复制代码
  Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
   JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
   {
    //日期类型默认格式化处理
     setting.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
      setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";

    //空值处理
      setting.NullValueHandling = NullValueHandling.Ignore;

      //高级用法九中的Bool类型转换 设置
      setting.Converters.Add(new BoolConvert("是,否"));

      return setting;
   });
复制代码

 

这样设置以后,以后使用序列化的地方就不需要单独设置了,个人最喜欢设置的是空值处理这一块。

总结

Newtonsoft.Json序列化库替我们想了很多特性,也实现了很多特性,除了上面介绍的几种高级用法外,还有其它的特殊用法,可以去官网进行学习。当然这里我目前最喜欢的特性就是那个忽略部分属性序列化的功能,很小的代码改动实现了接口的优化,提升了用户体验。

 

string.Format出现异常:输入字符串的格式不正确 Exception during StringFormat - jhlong - 博客园

mikel阅读(1510)

来源: string.Format出现异常:输入字符串的格式不正确 Exception during StringFormat – jhlong – 博客园

错误信息:Exception during StringFormat:输入字符串的格式不正确

“System.FormatException”类型的未经处理的异常在 mscorlib.dll 中发生

其他信息: 输入字符串的格式不正确。

string s = string.Format("{return:\"{0}\"}", result);//这里报错

后来发现是 占位符 result 里含有 “{}” 大括号导致。

解决办法:替换处理result,

用两个 { 或者 }连写表示单个。

或者其他办法

字符串的{改成{{和}改成}}

String code = “{{queryParam:{{\”Warehouse\”: [{{\”Code\”: \”\”}}],\”TimeBegin\”: \”{0}\”,\”TimeEnd\”: \”{1}\”}}”;

try
{
String d = DateTime.Now.AddMinutes(-300).ToString();
string dd = DateTime.Now.ToString();
code = String.Format(code,d,dd);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}

Visual Studio 2019 的安装 (附专业版和企业版产品密钥) - ZY-JIMMY - CSDN博客

mikel阅读(2370)

来源: Visual Studio 2019 的安装 (附专业版和企业版产品密钥) – ZY-JIMMY – CSDN博客

官网下载:
https://visualstudio.microsoft.com/zh-hans/

选择需要的功能即可开始安装:

安装成功后直接启动:

企业版安装后试用30天, 点击帮助 – 注册产品:

点击 使用产品密钥解锁

Visual Studio 2019 Enterprise Key:
BF8Y8-GN2QH-T84XB-QVY3B-RC4DF

Visual Studio 2019 Professional Key:
NYWVH-HT4XC-R2WYW-9Y3CM-X4V3Y

输入密钥,点击应用:

我们可以看到,产品已被解锁。

使用VS2019编写和调试Linux程序教程:
https://blog.csdn.net/ZYZMZM_/article/details/89044885
————————————————
版权声明:本文为CSDN博主「ZY-JIMMY」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZYZMZM_/article/details/89045810

C# Winform 跨线程更新UI控件常用方法汇总 - marshal-m - 博客园

mikel阅读(1088)

来源: C# Winform 跨线程更新UI控件常用方法汇总 – marshal-m – 博客园

概述

C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常。处理跨线程更新Winform UI控件常用的方法有4种:
1. 通过UI线程的SynchronizationContext的Post/Send方法更新;
2. 通过UI控件的Invoke/BeginInvoke方法更新;

3. 通过BackgroundWorker取代Thread执行异步操作;
4. 通过设置窗体属性,取消线程安全检查来避免”跨线程操作异常”(非线程安全,建议不使用)。
下文中对以上3种方法应用进行举例说明,希望能对初识C# Winform的同学们有些帮助。

成文表分享交流之意,惶恐水平有限,文中理解和表述有错误之处还请大家多被批评指正。

更新记录:

2018年2月3日,根据网友评论提示更新错别字,BegainInvoke=》BeginInvoke。

正文

1. 通过UI线程的SynchronizationContext的Post/Send方法更新

用法:

复制代码
        //共分三步
        //第一步:获取UI线程同步上下文(在窗体构造函数或FormLoad事件中)
        /// <summary>
        /// UI线程的同步上下文
        /// </summary>
        SynchronizationContext m_SyncContext = null;
        public Form1()
        {
            InitializeComponent();
            //获取UI线程同步上下文
            m_SyncContext = SynchronizationContext.Current;
            //Control.CheckForIllegalCrossThreadCalls = false;
        }
        //第二步:定义线程的主体方法
        /// <summary>
        /// 线程的主体方法
        /// </summary>
        private void ThreadProcSafePost()
        {
            //...执行线程任务

            //在线程中更新UI(通过UI线程同步上下文m_SyncContext)
            m_SyncContext.Post(SetTextSafePost, "This text was set safely by SynchronizationContext-Post.");

            //...执行线程其他任务
        }
        //第三步:定义更新UI控件的方法
        /// <summary>
        /// 更新文本框内容的方法
        /// </summary>
        /// <param name="text"></param>
        private void SetTextSafePost(object text)
        {
            this.textBox1.Text = text.ToString();
        }
        //之后,启动线程
        /// <summary>
        /// 启动线程按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void setSafePostBtn_Click(object sender, EventArgs e)
        {
            this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafePost));
            this.demoThread.Start();
        }
复制代码

说明:三处加粗部分是关键。该方法的主要原理是:在线程执行过程中,需要更新到UI控件上的数据不再直接更新,而是通过UI线程上下文的Post/Send方法,将数据以异步/同步消息的形式发送到UI线程的消息队列;UI线程收到该消息后,根据消息是异步消息还是同步消息来决定通过异步/同步的方式调用SetTextSafePost方法直接更新自己的控件了。

在本质上,向UI线程发送的消息并是不简单数据,而是一条委托调用命令。

//在线程中更新UI(通过UI线程同步上下文m_SyncContext)
m_SyncContext.Post(SetTextSafePost, “This text was set safely by SynchronizationContext-Post.”);
可以这样解读这行代码:向UI线程的同步上下文(m_SyncContext)中提交一个异步消息(UI线程,你收到消息后以异步的方式执行委托,调用方法SetTextSafePost,参数是“this text was ….”).

 

2.通过UI控件的Invoke/BeginInvoke方法更新

用法:与方法1类似,可分为三个步骤。

复制代码
        // 共分三步

        // 第一步:定义委托类型
        // 将text更新的界面控件的委托类型
        delegate void SetTextCallback(string text);

        //第二步:定义线程的主体方法
        /// <summary>
        /// 线程的主体方法
        /// </summary>
        private void ThreadProcSafe()
        {
            //...执行线程任务

            //在线程中更新UI(通过控件的.Invoke方法)
            this.SetText("This text was set safely.");

            //...执行线程其他任务
        }
        //第三步:定义更新UI控件的方法
        /// <summary>
        /// 更新文本框内容的方法
        /// </summary>
        /// <param name="text"></param>
        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the 
            // calling thread to the thread ID of the creating thread. 
            // If these threads are different, it returns true. 
            if (this.textBox1.InvokeRequired)//如果调用控件的线程和创建创建控件的线程不是同一个则为True
            {
                while (!this.textBox1.IsHandleCreated)
                {
                    //解决窗体关闭时出现“访问已释放句柄“的异常
                    if (this.textBox1.Disposing || this.textBox1.IsDisposed)
                        return;
                }
                SetTextCallback d = new SetTextCallback(SetText);
                this.textBox1.Invoke(d, new object[] { text });
            }
            else
            {
                this.textBox1.Text = text;
            }
        }
        //之后,启动线程
        /// <summary>
        /// 启动线程按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void setTextSafeBtn_Click(
            object sender,
            EventArgs e)
        {
            this.demoThread =
                new Thread(new ThreadStart(this.ThreadProcSafe));

            this.demoThread.Start();
        }
复制代码

说明:这个方法是目前跨线程更新UI使用的主流方法,使用控件的Invoke/BeginInvoke方法,将委托转到UI线程上调用,实现线程安全的更新。原理与方法1类似,本质上还是把线程中要提交的消息,通过控件句柄调用委托交到UI线程中去处理。

解决窗体关闭时出现“访问已释放句柄“的异常部分代码参考博客园-事理同学的文章

 

3.通过BackgroundWorker取代Thread执行异步操作

用法:

复制代码
        //共分三步

        //第一步:定义BackgroundWorker对象,并注册事件(执行线程主体、执行UI更新事件)
        private BackgroundWorker backgroundWorker1 =null;
        public Form1()
        {
            InitializeComponent();

           
            backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
            //设置报告进度更新
            backgroundWorker1.WorkerReportsProgress = true;
            //注册线程主体方法
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            //注册更新UI方法
            backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
            //backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
        }

        //第二步:定义执行线程主体事件
        //线程主体方法
        public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            //...执行线程任务

            //在线程中更新UI(通过ReportProgress方法)
            backgroundWorker1.ReportProgress(50, "This text was set safely by BackgroundWorker.");

            //...执行线程其他任务
        }
        //第三步:定义执行UI更新事件
        //UI更新方法
        public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.textBox1.Text = e.UserState.ToString();
        }
        //之后,启动线程
        //启动backgroundWorker
        private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e)
        {
            this.backgroundWorker1.RunWorkerAsync();
        }
复制代码

说明:C# Winform中执行异步任务时,BackgroundWorker是个不错的选择。它是EAP(Event based Asynchronous Pattern)思想的产物,DoWork用来执行异步任务,在任务执行过程中/执行完成后,我们可以通过ProgressChanged,ProgressCompleteded事件进行线程安全的UI更新。

需要注意的是://设置报告进度更新
backgroundWorker1.WorkerReportsProgress = true;
默认情况下BackgroundWorker是不报告进度的,需要显示设置报告进度属性。

 

4. 通过设置窗体属性,取消线程安全检查来避免”线程间操作无效异常”(非线程安全,建议不使用)

用法:将Control类的静态属性CheckForIllegalCrossThreadCalls为false。

      public Form1()
        {
            InitializeComponent();
            //指定不再捕获对错误线程的调用
            Control.CheckForIllegalCrossThreadCalls = false;
        }

说明:通过设置CheckForIllegalCrossThreadCalls属性,可以指示是否捕获线程间非安全操作异常。该属性值默认为ture,即线程间非安全操作是要捕获异常的(”线程间操作无效”异常)。通过设置该属性为false简单的屏蔽了该异常。Control.CheckForIllegalCrossThreadCalls的注释如下

复制代码
        //
        // 摘要:
        //     获取或设置一个值,该值指示是否捕获对错误线程的调用,这些调用在调试应用程序时访问控件的 System.Windows.Forms.Control.Handle
        //     属性。
        //
        // 返回结果:
        //     如果捕获了对错误线程的调用,则为 true;否则为 false。
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [SRDescription("ControlCheckForIllegalCrossThreadCalls")]
        [Browsable(false)]
        public static bool CheckForIllegalCrossThreadCalls { get; set; }
复制代码

综述:

文中介绍的4种方法,前三种是线程安全的 ,可在实际项目中因地制宜的使用。最后一种方法是非线程安全的,初学者可以实验体会但不建议使用它。

下面列表对比一下这四种方法

方法 线程安全 支持异步/同步 其他
UI SyncContext更新 Post/Send 尽量在窗体构造函数、FormLoad中获取同步上下文
控件Invoke control.Invoke/BeginInvoke 注意检查控件句柄是否已释放
BackgroundWorker更新

ProgressChanged、RunWorkerCompleted

事件同步更新

报告进度
CheckForIllegalCrossThreadCalls

取消跨线程调用检查

同步更新 简单,不建议使用

 

 

 

 

 

参考资料:

瑞奇特(Jeffrey Richter),《clr var C#》

MSDN, How to: Make Thread-Safe Calls to Windows Forms Controls