C#中的类型转换-自定义隐式转换和显式转换

来源:https://note.guoqianfan.com/2022/04/22/operator-implicit-explicit-in-csharp/

前言

有时我们会遇到这么一种情况:在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#

示例代码

//================定义类型和方法================
class Robot
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Robot(int id, string name)
    {
        Id = id;
        Name = name;
    }

    #region 其他类型->本类

    //隐式转换
    public static implicit operator Robot(string name)
    {
        return new Robot(101, name);
    }

    //显式转换
    public static explicit operator Robot(int id)
    {
        return new Robot(id, "miku");
    }

    #endregion

    #region 本类->其他类型

    //隐式转换
    public static implicit operator string(Robot robot)
    {
        return robot.Name;
    }

    //显式转换
    public static explicit operator int(Robot robot)
    {
        return robot.Id;
    }

    #endregion
}

//================测试代码================
#region 其他类型->本类

string gumiStr = "gumi";
Robot gumi001 = gumiStr; //隐式转换
Console.WriteLine("隐式转换:gumi001 : {0}", JsonConvert.SerializeObject(gumi001));

int lukaId = 1004;
Robot luka001 = (Robot)lukaId; //显式转换
Console.WriteLine("显式转换:luka001 : {0}", JsonConvert.SerializeObject(luka001));

#endregion

#region 其他类型->本类

Robot miku001 = new Robot(1001, "miku10001");
//隐式转换
string mikuName = miku001;
//显式转换
int mikuId = (int)miku001;

Console.WriteLine("隐式转换:miku001 Name: {0}", mikuName);
Console.WriteLine("显式转换:miku001 Id: {0}", mikuId);

#endregion

输出结果如下:

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

实际应用

问题

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

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

答案

using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
                    
public class Program
{
    public static void Main()
    {
        var json = "[1,[[2,2],[2,2],[2,2],[2,2]]]";
        var root = JsonConvert.DeserializeObject<Root>(json);
        foreach(var ele in root)
        {
            if(ele.SingleValue.HasValue)
            {//有值,原始数据为 1
                Console.WriteLine(ele.SingleValue.Value);
            }else
            {//原始数据为 二维数组
                Console.WriteLine(string.Join(" ",ele.Select(x=>string.Join(",",x))));
            }
        }
        Console.WriteLine(JsonConvert.SerializeObject(root));
    }
}

class Root : List<Element> { }
[JsonConverter(typeof(CConverter))]
class Element : List<List<long>>
{
    //该属性,存放 1 。后续可以通过判断该属性是否有值来得知原始数据的情况
    public long? SingleValue { get; set; }

    //遇到 1 ,隐式转换为 该类型,其中 1 被存放到SingleValue属性
    public static implicit operator Element(long d)
    {
        return new Element { SingleValue = d };
    }
}

public class CConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Element));
    }

    public override bool CanRead  { get { return false; } }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var ele = value as Element;
        var token = ele.SingleValue.HasValue ? JToken.FromObject(ele.SingleValue.Value) : JToken.FromObject(ele.ToList());
        token.WriteTo(writer);
    }

    public override bool CanWrite { get { return true; } }
}

报错

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

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

是因为有这个限制:类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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容