博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.NET架构小技巧(4)——反射,架构人员法宝II
阅读量:4034 次
发布时间:2019-05-24

本文共 19437 字,大约阅读时间需要 64 分钟。

上一篇博文中,利用属性反射的特点,用两个方法完成了字符转实体,实体转字符的工作,但有些复杂的场景,上面方法就没那么好用了,就需要更复杂的方式来组装处理。

先来看一个接口文档,下面是接口的调用方式

long  OltpTransData(unsigned long msgType,unsigned long packageType,      unsigned long packageLength,char *str,LPTSTR com);

I.msgType:业务请求类型;

II.packageType:数据解析格式类型,系统重组数据时使用

III.packageLength:数据串的长度;

IV. str:数据串;调用时,通过数据串传入参数;函数返回时,数据串中包含返回的数据,数据按字符串方式组合,并且在字符串第一位保留一个空格;另外,除了16位日期右补空格,其他的字段均以左空格补位

V. com:数据请求串口

业务请求类型

数据解析格式类型

数据串最小长度

说明

1001

101

126

实时验卡(读卡、验卡)

1002

12

610

实时结算

1003

7

291

实时明细数据传输

1004

9

253

实时住院登记数据传输

1005

8

309

实时医嘱数据传输

1006

12

610

实时结算预算

1007

2

1101

实时住院首次病程记录传输

1009

504

85

医师信息查询

1010

510

331

出入院标准传输

读卡

序号

定义

数据原型

起始位置

数据长度

备注

数据填充

1

个人编号

CHAR

1

8

医保编号,以’E’开头的为异地社保卡,详见说明

院端

2

姓名

CHAR

9

20

中心

3

身份证号

CHAR

29

18

18位或15位

中心

4

IC卡号

CHAR

47

9

院端

5

治疗序号

NUM

56

4

中心

6

职工就医类别

CHAR

60

1

A在职、B退休、L事业离休、T特诊、Q企业离休、E退老、N农民工、X未成年居民、O老年居民(老年居民、低收入人员、残疾人)、D低保人员、S  三无人员、U 大学生

中心

7

基本个人帐户余额

NUM

61

10

中心

8

补助个人帐户余额

NUM

71

10

现用于公务员单独列帐

中心

9

统筹累计

NUM

81

10

中心

10

门诊慢病统筹累计

NUM

91

10

中心

11

月缴费基数

NUM

101

10

月缴费工资

中心

12

帐户状态

CHAR

111

1

A正常、B半止付、C全止付、D销户

中心

13

参保类别1

CHAR

112

1

是否享受高额:

0 不享受高额、1  享受高额、2 医疗保险不可用

中心

14

参保类别2

CHAR

113

1

是否享受补助(商业补助、公务员补助):0  不享受、1 商业、2  公务员、3事业离休差额拨款人员

中心

15

参保类别3

CHAR

114

1

0 企保、1 事保、2企业慢病、3事业慢病、4异地就医,详见说明

中心

16

参保类别4

CHAR

115

1

0或2生育不可用、1或3生育可用

中心

17

参保类别5

CHAR

116

1

0工伤不可用、1工伤可用

中心

18

住院次数累计

NUM

117

4

中心

19

家床次数累计

NUM

121

4

中心

查询医师

序号

定义

数据原型

起始位置

数据长度

备注

填写方式

1

医师编码

CHAR

1

8

院端

2

医师姓名

CHAR

9

20

中心

3

身份证号

CHAR

29

18

中心

4

可出诊医院编号

CHAR

47

20

详见说明

中心

5

终止日期

DATETIME

67

16

YYYYMMDDHHMMSS

中心

6

有效标志

CHAR

83

1

‘0’无效,‘1’有效

中心

这个接口是拼接方式,每个数据项都有固定的长度,有些数据也有固定的格式,比如日期,还有很多类型,都是有固定值固定含义的。这种情况下需要更多的信息来告诉两个转换方法该怎么转换,这里就引出了Attribute——一个专门来给类或属性方法加特征的知识点。

///     /// 发送报文类型    ///     [AttributeUsage(AttributeTargets.Class)]    public class PackageTypeAttribute : Attribute    {        ///         /// 发关报文实体类属性特性类        ///         /// 属性的序号,从1开始        /// 属性转成字符串后长度        public PackageTypeAttribute(uint OperationType, uint DataFormaterType, uint MinLength)        {            this.OperationType = OperationType;            this.DataFormaterType = DataFormaterType;            this.MinLength = MinLength;        }        ///         /// 业务请求类型        ///         public uint OperationType        { get; private set; }        ///         /// 数据解析格式类型        ///         public uint DataFormaterType        { get; private set; }        ///         /// 数据串最小长度        ///         public uint MinLength        { get; private set; }    }    ///     /// 报文中属性的顺序SN和长度Length    ///     [AttributeUsage(AttributeTargets.Property)]    public class PackageAttribute : Attribute    {        ///         /// 序号,从1开始        ///         public int SN        { get; private set; }        ///         /// 转成字符串后的长度        ///         public int Length        { get; private set; }        ///         /// 发关报文实体类属性特性类        ///         /// 属性的序号,从1开始        /// 属性转成字符串后长度        public PackageAttribute(int SN, int Length)        {            this.SN = SN;            this.Length = Length;        }        ///         /// 是否是时间类型,因为时间类型是左对齐,右补空格        ///         public bool IsDateTime        { get; set; }    }    ///     /// 取枚举的值还是    ///     [AttributeUsage(AttributeTargets.Enum)]    public class EnumValeuNumberAttribute : Attribute    {        ///         /// 是否把枚举类型属性的的值数转成Char类型        ///         public bool IsChar        { get; set; }    }

定义了三个特性类,PackageTypeAttribute主用来区分不同的交易类型,从实体类上获取不同交易的函数参数;PackageAttribute是在实体类的属性上的,是核心特性类,它标记了属性的序号,和每个属性的长度,和属性是否是DateTime类型;EnumValeuNumberAttribute是用来专门处理枚举类型的属性的。

///     /// 医师信息查询    ///     [PackageType(1009, 504, 85)]    public class DoctorQuery : Entity    {        ///         /// 医师编码        ///         [Package(1, 8)]        public virtual String DoctorCode        {            get; set;        }        ///         /// 医师姓名        ///         [Package(2, 20)]        public virtual String DoctorName        { get; set; }        ///         /// 身份证号        ///         [Package(3, 18)]        public virtual string PersonID        { get; set; }        ///编号为0002的医院, 下属有编号为0113的定点, 在总院注册登记的医师可以在这样的2家医院出诊, 则“可出诊医院编号”为00020113,若长度不足20位则前补空格。        ///         /// 可出诊医院编号        ///         [Package(4, 20)]        public virtual string CanVisitHospitalCode        { get; set; }        ///         /// 终止日期        ///         [Package(5, 16, IsDateTime = true)]        public virtual string TerminationTime        { get; set; }        ///         /// 有效标志        ///         [Package(6, 1)]        public virtual DLYBAvailableMarker DLYBAvailableMarker        { get; set; }    }    ///     /// 有效标志    ///     [EnumValeuNumber]    public enum DLYBAvailableMarker    {        ///         /// 无效        ///         nullity = 0,        ///         /// 有效        ///         Valid = 1    }    ///     /// 实时验卡(读卡、验卡)    ///     [PackageType(1001, 101, 126)]     public  class QueryCardEntity : Entity    {                ///         /// 个人编号        ///         [Package(1, 8)]        public virtual string PersonNumber        { get; set; }        ///         /// 姓名        ///         [Package(2, 20)]        public virtual string Name        { get; set; }        ///         /// 身份证号        ///         [Package(3, 18)]        public virtual string PersonID        { get; set; }        ///         /// IC卡号        ///         [Package(4, 9)]        public virtual string ICCardNumber        { get; set; }        long therapyNumber;        ///         /// 治疗序号        ///         [Package(5, 4)]        public virtual long TherapyNumber        {            get            {                return therapyNumber;            }            set            {                if (value >= 0 && value <= 9999)                {                    therapyNumber = value;                }                else                {                    throw new Exception("治疗号在0-9999之间");                }            }        }        ///         /// 职工就医类别        ///         [Package(6, 1)]        public virtual string TherapyCategory        { get; set; }        ///         /// 基本个人帐户余额        ///         [Package(7, 10)]        public virtual decimal BasePersonBalance        { get; set; }        ///         /// 补助个人帐户余额        ///         [Package(8, 10)]        public virtual decimal SubsidyPersonBalance        { get; set; }        ///         /// 统筹累计        ///         [Package(9, 10)]        public virtual decimal PlannerTotal        { get; set; }        ///         /// 门诊慢病统筹累计///新        ///         [Package(10, 10)]        public virtual decimal MZSlowDisease        { get; set; }        ///         /// 月缴费基数        ///         [Package(11, 10)]        public virtual decimal BaseFeeByMonth        { get; set; }        ///         /// 帐户状态        ///         [Package(12, 1)]        public virtual string AccoutState        { get; set; }        ///         /// 参保类别1        ///         [Package(13, 1)]        public virtual string InsuranceCategory1        { get; set; }        ///         /// 参保类别2        ///         [Package(14, 1)]        public virtual string InsuranceCategory2        { get; set; }        ///         /// 参保类别3        ///         [Package(15, 1)]        public virtual string InsuranceCategory3        { get; set; }        ///         /// 参保类别4        ///         [Package(16, 1)]         public virtual string InsuranceCategory4        { get; set; }        ///         /// 参保类别5        ///         [Package(17, 1)]        public virtual string InsuranceCategory5        { get; set; }        ///         /// 住院次数累计新        ///         [Package(18, 4)]        public virtual int ZYAddNumber        { get; set; }        ///         /// 家床次数累计新        ///         [Package(19, 4)]        public virtual int AddBedNumber        { get; set; }    }

上面的实体类分别使用了特性类,参照文档就OK。

public static class StringExtension    {          ///         /// 右边不够长度补空格,汉字算两个空格        ///         ///         /// 设定长度        /// 
public static string ChineseCharacterLeft(this string str, int length) { var len = Encoding.Default.GetBytes(str).Length; if (len < length) { for (int i = 0; i < length - len; i++) { str = " " + str; } } return str; } /// /// 右边不够长度补空格,汉字算两个空格 /// /// /// 设定长度 ///
public static string ChineseCharacterRight(this string str, int length) { var len = Encoding.Default.GetBytes(str).Length; if (len < length) { for (int i = 0; i < length - len; i++) { str += " "; } } return str; } /// /// 切除字符串 /// public static string ChineseCharacterSubstring(this string str, int length, out string remaining) { var arr = Encoding.Default.GetBytes(str); var barr = arr.Take(length).ToArray(); var valuestr = Encoding.Default.GetString(barr); barr = arr.Skip(length).ToArray(); remaining = Encoding.Default.GetString(barr); ; return valuestr; } }

‍上面代码是对某些属性的对齐方式作了处理。

///     /// 报文类的父类    ///     public abstract class Entity    {        ///         /// 组装发送报文格式        ///         /// 
public override string ToString() { var pros = this.GetType().GetProperties(); var sortPro = new SortedList
(); foreach (var pro in pros) { foreach (var att in pro.GetCustomAttributes(false)) { if (att is PackageAttribute) { var packageAtt = att as PackageAttribute; sortPro.Add(packageAtt.SN, pro); } } } var content = new StringBuilder(); #region 组合发送字符串 //遍历属性 foreach (var pro in sortPro) { //遍历属性上的特性 foreach (var att in pro.Value.GetCustomAttributes(false)) { //判断是否为自定义的PackageAttribute类型 if (att is PackageAttribute) { //转换属性上的特性类 var packageAtt = att as PackageAttribute; //取拼接时字符长度 var length = packageAtt.Length; //取属性的值 var proValue = pro.Value.GetValue(this, new Object[0]); //对decimal作处理 if (pro.Value.PropertyType.Name.ToLower() == "decimal") { proValue = Math.Round(Convert.ToDecimal(proValue), 2); if (Encoding.Default.GetByteCount(proValue.ToString()) > length) { proValue = "0"; } } //判断字符串长度过长 if (proValue != null && (pro.Value.PropertyType.Name.ToLower() == "string")) { if (System.Text.Encoding.Default.GetBytes(proValue.ToString()).Length > length) { throw new Exception(string.Format("属性{0}的值{1},长度超过{2}", pro.Value.Name, proValue, length)); } } //如果值为非空 if (proValue != null) { //日期是右补空格,其他是左补空格 if (!packageAtt.IsDateTime) { //这里注册,有些属性是枚举类型,有些属性拼接枚举的值,有些取枚举值对应的枚举数值,这里是从该属性类型上的EnumValeuNumberAttribute特性的IsValue属性来判断的,IsValue为true,就取枚举的值,为false取该值对应的枚举数 if (pro.Value.PropertyType.IsEnum) { foreach (var eatt in pro.Value.PropertyType.GetCustomAttributes(false)) { if (eatt is EnumValeuNumberAttribute) { var enumVaNu = eatt as EnumValeuNumberAttribute; if (enumVaNu.IsChar) { var enumNumber = ((char)(int)Enum.Parse(pro.Value.PropertyType, proValue.ToString())).ToString(); content.Append(enumNumber.ChineseCharacterLeft(length)); } else { var enumNumber = ((int)Enum.Parse(pro.Value.PropertyType, proValue.ToString())).ToString(); content.Append(enumNumber.ChineseCharacterLeft(length)); } } } } else { content.Append(proValue.ToString().ChineseCharacterLeft(length)); } } else//日期类型右补空格 { content.Append(proValue.ToString().ChineseCharacterRight(length)); } } else { content.Append("".ChineseCharacterLeft(length)); } } } } #endregion return content.ToString(); } ///
/// 把一个字符串转成一个对象 /// ///
///
public Entity ToEntity(Type entityType,string content) { var pros = entityType.GetProperties(); //按照特性类上的SN序号把属性名存入集合proPackageList中 List
proPackageList = new List
(pros.Length);            //初始化属性集合 for (int i = 0; i < pros.Length; i++) { foreach (var att in pros[i].GetCustomAttributes(false)) { if (att is PackageAttribute) { proPackageList.Add(null); break; } } } //按属性顺序排列属性 foreach (var pro in pros) { foreach (var att in pro.GetCustomAttributes(false)) { if (att is PackageAttribute) { var packageAtt = att as PackageAttribute; var index = packageAtt.SN - 1; proPackageList[index] = pro; } } } //创建实体对象 var constructor = entityType.GetConstructor(new Type[0]);            var entity = constructor.Invoke(new object[0]); foreach (var pro in proPackageList) { //遍历属性上的特性 foreach (var att in pro.GetCustomAttributes(false)) { //判断是否为自定义的PackageAttribute类型 if (att is PackageAttribute) { //转换属性上的特性类 var packageAtt = att as PackageAttribute; var length = packageAtt.Length; var valuestr = content.ChineseCharacterSubstring(length, out content).Trim(); if (pro.PropertyType.IsEnum) { foreach (var eatt in pro.PropertyType.GetCustomAttributes(false)) { if (eatt is EnumValeuNumberAttribute) { var eat = eatt as EnumValeuNumberAttribute; if (eat.IsChar) { var chr = Convert.ToChar(valuestr); var value = Convert.ChangeType(Enum.Parse(pro.PropertyType, ((int)chr).ToString()), pro.PropertyType); pro.SetValue(entity, value, null); } else { var value = Convert.ChangeType(Enum.Parse(pro.PropertyType, valuestr), pro.PropertyType); pro.SetValue(entity, value, null); } break; } } } else { var value = Convert.ChangeType(valuestr, pro.PropertyType); pro.SetValue(entity, value, null); } } } } return (Entity)entity; } }

这两个方法核心里通过反射属性上的特性,取特性中定义的固定值,来生成接口要求的字符串,合理的设计特性,可以使两个转换方法更优雅,更简便,在开发过程中,也需要不断调整理,适配,逐渐完善。

可以用下面的代码完成测试

using System;namespace ArchitectureDemo04{    class Program    {        static void Main(string[] args)        {            var backQueryCard = Send(new QueryCardEntity { PersonNumber = "0000001", ICCardNumber = "C00000001" });            var backDoctorQuery = Send(new DoctorQuery { DoctorCode = "0001" });        }        ///         /// 发送        ///         ///         /// 
static Entity Send(Entity entity) { try { foreach (var att in entity.GetType().GetCustomAttributes(false)) { if (att is PackageTypeAttribute) { var attPackage = att as PackageTypeAttribute; Console.WriteLine($"入参:"); Console.WriteLine(entity); Console.WriteLine("模拟函数调用:"); Console.WriteLine($"OltpTransData({attPackage.OperationType},{attPackage.DataFormaterType},{attPackage.MinLength},{entity})"); var backContent = BackOperation(entity); var backEntity = entity.ToEntity(entity.GetType(),backContent); return backEntity; } } return null; } catch { throw; } } /// /// 模拟医保中心返回 /// /// 参数 ///
static string BackOperation(Entity entity) { switch (entity.GetType().Name) { case "QueryCardEntity": return " 0000001 Jack210213198411113111C00000001 1A 1000.66 0 0 0 1800A00131 0 0"; case "DoctorQuery": return " 0001 DcotorLi210211198707182233 0002011320201029190850 1"; } return null; } }}

转载地址:http://vskdi.baihongyu.com/

你可能感兴趣的文章
Phone双模修改涉及文件列表
查看>>
android UI小知识点
查看>>
Android之TelephonyManager类的方法详解
查看>>
android raw读取超过1M文件的方法
查看>>
ubuntu下SVN服务器安装配置
查看>>
MPMoviePlayerViewController和MPMoviePlayerController的使用
查看>>
CocoaPods实践之制作篇
查看>>
[Mac]Mac 操作系统 常见技巧
查看>>
苹果Swift编程语言入门教程【中文版】
查看>>
捕鱼忍者(ninja fishing)之游戏指南+游戏攻略+游戏体验
查看>>
iphone开发基础之objective-c学习
查看>>
iphone开发之SDK研究(待续)
查看>>
计算机网络复习要点
查看>>
Variable property attributes or Modifiers in iOS
查看>>
NSNotificationCenter 用法总结
查看>>
C primer plus 基础总结(一)
查看>>
剑指offer算法题分析与整理(一)
查看>>
剑指offer算法题分析与整理(三)
查看>>
部分笔试算法题整理
查看>>
Ubuntu 13.10使用fcitx输入法
查看>>