前言

有时我们会遇到这么一种情况:在json数据里,数组里的数据类型不一致,导致我们不能直接反序列化为目标类型。最终我们只能反序列化为JObject类型,然后通过字符串取值的方式来取出数据。

下面介绍一种新方式:通过自定义隐式转换,把不一样的数据类型反序列化为一样的数据类型。

基础知识

类型转换有2种:隐式转换和显式转换。但是,不管是隐式转换,还是显式转换,都是生成了一个新对象返回的。改变新对象的属性,不会影响老对象!(dynamic对象除外,详情搜索dynamic动态类型)

自定义隐式/显式转换的方法需要用到几个关键字:implicit(隐式转换)、explicit(显式转换)、operator(操作符)。更多的注意点见下:

  1. 方法必須是static
  2. 使用implicitexplicit
  3. 搭配operator(此也是c#關鍵字,可在類別或結構宣告內多載內建運算子或提供使用者定義的轉換)
  4. 返回值为要转换为的目标类型,但不要在方法上声明,方法名目标类型。注意:返回值不一定是本类类型。本类型和其他类型之间可以互相转换,只要定义转换方法就行。
  5. 参数为原始类型,方法名目标类型
  6. 类A到类B的类型转换定义不能在类C中进行(即2个类的转换不能在第3个类中定义),否则会报错:用户定义的转换必须是转换成封闭类型,或者从封闭类型转换。具体查看后面的用户定义的转换必须是转换成封闭类型,或者从封闭类型转换
  7. 不能被virtual/override修饰(不能“覆盖”运算符,因为它们是静态的。)Overriding implicit operators in C#

示例代码

  1. //================定义类型和方法================
  2. class Robot
  3. {
  4. public int Id { get; set; }
  5. public string Name { get; set; }
  6. public Robot(int id, string name)
  7. {
  8. Id = id;
  9. Name = name;
  10. }
  11. #region 其他类型->本类
  12. //隐式转换
  13. public static implicit operator Robot(string name)
  14. {
  15. return new Robot(101, name);
  16. }
  17. //显式转换
  18. public static explicit operator Robot(int id)
  19. {
  20. return new Robot(id, "miku");
  21. }
  22. #endregion
  23. #region 本类->其他类型
  24. //隐式转换
  25. public static implicit operator string(Robot robot)
  26. {
  27. return robot.Name;
  28. }
  29. //显式转换
  30. public static explicit operator int(Robot robot)
  31. {
  32. return robot.Id;
  33. }
  34. #endregion
  35. }
  36. //================测试代码================
  37. #region 其他类型->本类
  38. string gumiStr = "gumi";
  39. Robot gumi001 = gumiStr; //隐式转换
  40. Console.WriteLine("隐式转换:gumi001 : {0}", JsonConvert.SerializeObject(gumi001));
  41. int lukaId = 1004;
  42. Robot luka001 = (Robot)lukaId; //显式转换
  43. Console.WriteLine("显式转换:luka001 : {0}", JsonConvert.SerializeObject(luka001));
  44. #endregion
  45. #region 其他类型->本类
  46. Robot miku001 = new Robot(1001, "miku10001");
  47. //隐式转换
  48. string mikuName = miku001;
  49. //显式转换
  50. int mikuId = (int)miku001;
  51. Console.WriteLine("隐式转换:miku001 Name: {0}", mikuName);
  52. Console.WriteLine("显式转换:miku001 Id: {0}", mikuId);
  53. #endregion

输出结果如下:

  1. 隐式转换:gumi001 : {"Id":101,"Name":"gumi"}
  2. 显式转换:luka001 : {"Id":1004,"Name":"miku"}
  3. 隐式转换:miku001 Name: miku10001
  4. 显式转换:miku001 Id: 1001

实际应用

问题

[1,[[2,2],[2,2],[2,2],[2,2]]]

这样一个字符串,如何可以反序列化成一个对象?(如何定义这个类?)

答案

  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using Newtonsoft.Json;
  5. using Newtonsoft.Json.Linq;
  6. public class Program
  7. {
  8. public static void Main()
  9. {
  10. var json = "[1,[[2,2],[2,2],[2,2],[2,2]]]";
  11. var root = JsonConvert.DeserializeObject<Root>(json);
  12. foreach(var ele in root)
  13. {
  14. if(ele.SingleValue.HasValue)
  15. {//有值,原始数据为 1
  16. Console.WriteLine(ele.SingleValue.Value);
  17. }else
  18. {//原始数据为 二维数组
  19. Console.WriteLine(string.Join(" ",ele.Select(x=>string.Join(",",x))));
  20. }
  21. }
  22. Console.WriteLine(JsonConvert.SerializeObject(root));
  23. }
  24. }
  25. class Root : List<Element> { }
  26. [JsonConverter(typeof(CConverter))]
  27. class Element : List<List<long>>
  28. {
  29. //该属性,存放 1 。后续可以通过判断该属性是否有值来得知原始数据的情况
  30. public long? SingleValue { get; set; }
  31. //遇到 1 ,隐式转换为 该类型,其中 1 被存放到SingleValue属性
  32. public static implicit operator Element(long d)
  33. {
  34. return new Element { SingleValue = d };
  35. }
  36. }
  37. public class CConverter : JsonConverter
  38. {
  39. public override bool CanConvert(Type objectType)
  40. {
  41. return (objectType == typeof(Element));
  42. }
  43. public override bool CanRead { get { return false; } }
  44. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  45. {
  46. throw new NotImplementedException();
  47. }
  48. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  49. {
  50. var ele = value as Element;
  51. var token = ele.SingleValue.HasValue ? JToken.FromObject(ele.SingleValue.Value) : JToken.FromObject(ele.ToList());
  52. token.WriteTo(writer);
  53. }
  54. public override bool CanWrite { get { return true; } }
  55. }

报错

用户定义的转换必须是转换成封闭类型,或者从封闭类型转换

这个错误,与封闭类型无关

是因为有这个限制:类A到类B的类型转换定义不能在类C中进行(即2个类的转换不能在第3个类中定义)

所以对于目标类型是集合类List<T>,我们无法直接定义到它的转换。不过,有2个迂回的方法:

  • 创建个类继承自集合类List<T>,定义到这个子类的转换。上面实际应用中的代码就是这样做的:class Element : List<List<long>>
  • 创建T1T2的自定义转换,使用时逐个转换:list.Select(p=>(B)p).ToList()

参考

  1. 隐式转换:用户定义的转换必须是转换成封闭类型,或者从封闭类型转换:https://blog.csdn.net/kamui_shiron/article/details/8807142

其他

应用和设计

在定義類別時,如果有需要,就可以使用這兩個關鍵字來提供類別一些額外的功能

但在使用時也必須考慮設計上是否合理

例如當兩類別有相關性時是否該提取出父类或是接口來使用,而不是為了方便做了一堆轉換,導致程式撰寫與維護上的困難。

读音

  • 隐式转换:implicit [ɪmˈplɪsɪt] adj.不言明[含蓄]的; 无疑问的,绝对的; 成为一部份的; 内含的;
  • 显式转换:explicit [ɪkˈsplɪsɪt] adj.明确的,清楚的; 直言的; 详述的; 不隐瞒的;

参考

  1. 【问】这样一个字符串如何反序列化:http://www.newsmth.net/nForum/#!article/DotNET/69817
  2. 型別轉換關鍵字explicit與implicit的用法:https://dotblogs.com.tw/lastsecret/2011/11/14/57875
  3. c#关键词implicit和explicit:https://blog.csdn.net/Joyhen/article/details/40110391

C#中的类型转换-自定义隐式转换和显式转换的更多相关文章

  1. Java基础学习-类型转换之隐式转换

    +是一个运算符,我们应该能够看懂,做数据的加法. boolean类型不能转换为其他的数据类型. 默认转换:     byte,short,char--int--float--double     by ...

  2. scala自定义隐式转换

    Scala自定义隐式转换 一.编写隐式转换类 /** * Author Mr. Guo * Create 2019/4/20 - 17:40 */ object StringImprovments { ...

  3. 使用自定义隐式转换快速创建失败Result

    系统要求方法都返回 Result 结果,通常我们会如此定义一个 Result 1 public class Result<T> 2 { 3 public virtual int Code ...

  4. VB.Net隐式转换和显式转换的方法(转)

    VB.Net隐式转换和显式转换的方法(转) “隐式转换”不需要源代码中的任何特殊语法.在下面的示例中,在将 k 的值赋给 q 之前,Visual Basic 将该值隐式转换成单精度浮点值.   Dim ...

  5. 自己定义隐式转换和显式转换c#简单样例

    自己定义隐式转换和显式转换c#简单样例 (出自朱朱家园http://blog.csdn.net/zhgl7688) 样例:对用户user中,usernamefirst name和last name进行 ...

  6. F#中的自定义隐式转换

    我们知道隐式变换在可控情况下会使代码变得简洁.熟悉C#的都知道C#中可以自定义隐式变换,例如 public class A { private int data; public static impl ...

  7. ahjesus自定义隐式转换和显示转换

    implicit    关键字用于声明隐式的用户定义类型转换运算符. 如果可以确保转换过程不会造成数据丢失,则可使用该关键字在用户定义类型和其他类型之间进行隐式转换. 参考戳此 explicit    ...

  8. C++隐式转换与显式转换

    普通类型的转换顺序:隐式把char——>int和从short——>double.转换可能会导致数据的丢失. 自定义类型:有两种函数可以进行隐式转换,单参数构造函数 和 隐式类型转换符.   ...

  9. c# 强制转换, 隐式转换, 显式转换

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

随机推荐

  1. Android SugarORM(1)

    Android Sugar ORM (1) Android Sugar ORM比我之前用过的ORM都要简单许多, 其目的是简化与Android中SQLite数据库的交互, 优点如下: 消除了编写SQL ...

  2. Python "爬虫"出发前的装备之一正则表达式

    1. 正则表达式 正则表达式是一种模板表达式语言 通过定义规则去匹配.查找.替换.分割一个长字符串中特定的子字符信息. 如在一篇文章中查找出所有合法的电子邮箱地址,则可以先用正则表达式定义一个电子邮箱 ...

  3. 『德不孤』Pytest框架 — 7、Pytest预期失败

    目录 1.@pytest.mark.xfail()标记的作用 2.应用场景 3.语法参数说明 4.示例 5.忽略xfail标识 Pytest预期失败需要使用@pytest.mark.xfail()标记 ...

  4. 【C# 线程】Thread类 以及使用案例

    System.Threading.Thread类 涉及到的类和枚举 Volatile 类Interlocked 类SpinLock 类SpinWait类Barrier 类ThreadLocal< ...

  5. 开发Windows程序的三种方式

    软件开发方式一共有三种:SDK方式.MFC开发方式.托管环境的开发都是基于消息的开发 SDK方式 原装api的调用SDK方式使用C语言和Windows应用程序编程接口(Windows API)来开发W ...

  6. idea maven: invalid class root

    maven导入依赖没有问题,但是项目lib的类飘红,实际导入的类中有一些没有,这是idea的问题,重启idea,发现需要重新导入项目,重新导入后问题解决. 原因:可能之前误删了项目中idea的一些文件 ...

  7. 简明Python教程 - 学习笔记

    1,第一步 cmd下运行python文件 :python 文件名.(在文件目录下) 退出python解释器:ctrl + z 或 exit() 回车 进入帮助模式:help(),退出:q 或 回车 2 ...

  8. 【.NET6+WPF】WPF使用prism框架+Unity IOC容器实现MVVM双向绑定和依赖注入

    前言:在C/S架构上,WPF无疑已经是"桌面一霸"了.在.NET生态环境中,很多小伙伴还在使用Winform开发C/S架构的桌面应用.但是WPF也有很多年的历史了,并且基于MVVM ...

  9. JZ-022-从上往下打印二叉树

    从上往下打印二叉树 题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 题目链接: 从上往下打印二叉树 代码 import java.util.ArrayList; import jav ...

  10. fibnacci数列的python实现

    费波那契数列(Successione di Fibonacci) 又译为费波拿契数.斐波那契数列.费氏数列.黄金分割数列 在数学上,费波那契数列是以递归的方法来定义: 用文字来说,就是费波那契数列由0 ...