C#中implicit和explicit

news2024/11/29 2:42:15

理解:

  • 使用等号代替构造函数调用的效果
  • 以类似重载操作符的形式定义用于类型转换的函数
  • 前者类型转换时候直接写等号赋值语法,后者要额外加目标类型的强制转换
  • stirng str -> object o -> int a 可以 int a = (int)(str as object)转换通过编译,但没有转换逻辑所以运行会报错,explicit就可以解决这个问题,类似 于dart语言中的  源类型对象.to目标类型() 的意思.

什么是隐式转换?

int i = 1;

double d = i;

什么是显式转换?

double d = 1;

int i = (int)d

implicit

隐式转换,相当于封箱操作如: int i = 1; object obj = i;

如果不理解封箱可以换个例子: int i = 1; double d = i;

double的可取值范围包含了int的全部可取值范围,所以可以int->double隐式转换

explicit

显式转换,相当于拆箱操作如: object obj = 1; int i = (int)obj;

如果不理解封箱可以换个例子: double i = 1; int i = (int) d;


为什么有显式和隐式转换?

在C#本身的类型转换中,

int, uint , float之类的都可以隐式转换到double,是因为 double可以不丢失精度的情况下保存int, uint , float之类类型的值

而 double要想转换回到其他数值类型,则有可能丢失精度,所以我们要强制转换,也就是显式的指定转换的方式,即

double d = 1.1;

//int i = d;//这样不可以转换

int i = (double)d; //这样可以转换,但是会丢精度

此时 i 的值为1而不是1.1(向下取整,即Math.Floor()函数执行后再转为int的相同作用.

故,

方便,是隐式转换的主要目的

明确,精确,安全,是显式转换的主要目的


implicit/explicit的用法

固定以 public static implicit/explicit operator 目标类型(源类型 源类型形参)

的方式使用.

假设我们有人民币Rmb和美元Dollar类

评估资产的时候,默认都用Dollar来作为通用单位,当需要换成Rmb或者其他比重比如Krw的时候,需要显式的,明确的知道要转换的目标币种的类型.

比如隐式转换 Rmb->Dollar

public static implicit operator Dollar(Rmb rmb)

{

        //汇率相关的计算

}

再比如显式转换 Dollar->Rmb

public static explicit operator Rmb(Dollar rmb)

{

        //汇率先关的计算

}

使用时,隐式转换直接用Rmb可以直接给Dollar赋值

var rmb = new Rmb();

var dollar = rmb;//可以正常编译通过并在运行时正确转换

var doaalr = new Dollar();

//var rmb = dollar;//不可以通过编译,因为没有实现从dollar到rmb的隐式转换

var rmb = (Rmb)dollar;//可以通过编译并在运行时正确转换.


设计建议:

不管隐式还是显式转换都要保证类型安全不溢出不抛错

隐式转换设计时尽量源类型和目标类型不要有太多的偏差,否则容易造成歧义,如

var person = new Person(){ Id = 1111 };

int id = person; //这种虽然是方便从person中取出Id属性赋值给id,但理解可能会有偏差.

尽管我们可以用

int personId = person; 仍然是有较大的歧义.读代码的人会想id怎么会是一个"人"对象呢?

所以都不如 var id = person.Id来的直观.

像public class ArgsInfo内定义一个隐式转换从 string [] 到 ArgsInfo就是一个较好的设计

//一段精简的示例代码,实际设计会比这个健壮,仅为了表示该类和 string[] args较好耦合.

public class ArgsInfo
	{
		private readonly Dictionary<string,string> _args = new();
		public static implicit operator ArgsInfo(string[] args)
		{
			var result = new ArgsInfo();
			foreach (var arg in args)
			{
				var kv = arg.Split('=');
				if (kv.Length == 2)
				{
					result._args[kv[0]] = kv[1];
				}
			}
			return result;
		}

		public override string ToString()
		{
			var sb = new StringBuilder();
			foreach (var (key, value) in _args)
			{
				sb.AppendLine($"{key}={value}");
			}
			return sb.ToString();
		}
	}

	public static void Main(string[] args)
	{
        //就像调用了 var argsInfo = new ArgsInfo(args);
		ArgsInfo argsInfo = args;
		Console.WriteLine(argsInfo);
	}

显示转换设计时, 语义会更明确, 但必要的类型转换说明不可少.丢不丢精度,等都要写清

由于不像函数ConvertXXXToYYY, src.ToDestType<int>(), src.toInt()等直观的通过名称就知道含义且可以传递各种参数如精度之类的,所以保证类型转换的安全稳定和易维护拓展很重要.


完整示例代码带注释:

新建一个cs文件,直接运行Test方法看看效果吧

Rider截图:

/*


 implicit:隐式转换
 explicit:显式转换


*/

using System.Globalization;

namespace CS2TS.Test._1_InTestCSFiles;

/// <summary>
///     常量值定义
/// </summary>
public static class Constant
{
	/// <summary>
	///     1美元换多少人民币
	/// </summary>
	public const double DollarToRmb = 6.5;

	/// <summary>
	///     1美元换多少韩元
	/// </summary>
	public const double DollarToKrw = 1100;
}

/// <summary>
///     人
/// </summary>
public class Person
{
	/// <summary>
	///     名字
	/// </summary>
	public string Name { get; set; } = "无名氏";

	/// <summary>
	///     资产,默认以可隐式转换的美元表示,转换成其他货币需要显式转换标明意图
	/// </summary>
	public Dollar Money { get; set; } = new();
}

public class Rmb
{
	public double RmbAmount { get; set; }

	public static implicit operator Dollar(Rmb rmb)
	{
		return new Dollar
		{
			DollarAmount = rmb.RmbAmount / Constant.DollarToRmb
		};
	}

	public static implicit operator Rmb(Dollar dollar)
	{
		return new Rmb
		{
			RmbAmount = dollar.DollarAmount * Constant.DollarToRmb
		};
	}

	public static explicit operator Krw(Rmb rmb)
	{
		//先把rmb转换成dollar,然后再转换成krw
		return (Dollar)rmb;
	}

	public static explicit operator Rmb(Krw krw)
	{
		//先把krw转换成dollar,然后再转换成rmb
		return (Dollar)krw;
	}
}

/// <summary>
///     韩元
/// </summary>
public class Krw
{
	public double KrwAmount { get; set; }

	/*

	 可以直接用美元换韩元
	 如果换成人民币则需要显式转换

    */

	public static implicit operator Dollar(Krw krw)
	{
		return new Dollar
		{
			DollarAmount = krw.KrwAmount / Constant.DollarToKrw
		};
	}

	public static implicit operator Krw(Dollar dollar)
	{
		return new Krw
		{
			KrwAmount = dollar.DollarAmount * Constant.DollarToKrw
		};
	}

	public static explicit operator Rmb(Krw krw)
	{
		//先把krw转换成dollar,然后再转换成rmb
		return (Dollar)krw;
	}

	public static explicit operator Krw(Rmb rmb)
	{
		//先把rmb转换成dollar,然后再转换成krw
		return (Dollar)rmb;
	}
}

/// <summary>
///     美元,作为中间货币,人民币和韩元都可以直接换成美元,但是美元要换成什么,需要显式转换
/// </summary>
public class Dollar
{
	public double DollarAmount { get; set; }

	public override string ToString()
	{
		return DollarAmount.ToString(CultureInfo.InvariantCulture);
	}
}

public class ImplicitAndExplicit
{
	public static void Test()
	{
		#region 小明,美元换韩元和人民币

		var ming = new Person
		{
			Name = "小明",
			Money = new Dollar
			{
				DollarAmount = 1000
			}
		};
		//他想换成韩元的或者人民币的时候,需要显示的转换
		var rmbOfMing = (Rmb)ming.Money;
		Console.WriteLine($"小明有{ming.Money.DollarAmount}美元,换成人民币是{rmbOfMing.RmbAmount}元");
		var krwOfMing = (Krw)ming.Money;
		Console.WriteLine($"小明有{ming.Money.DollarAmount}美元,换成韩元是{krwOfMing.KrwAmount}元");

		#endregion

		#region 小红,人民币换韩元

		Console.WriteLine("小红只有人民币1000元,但是出国换货币的时候,都是央行的汇率,所以她的人民币要先转换成美元,然后再转换成其他货币");
		var rmbOfHong = new Rmb
		{
			RmbAmount = 1000
		};
		var hong = new Person
		{
			Name = "小红",
			// 不需要显示转换,因为Rmb有implicit转换成Dollar
			Money = rmbOfHong
		};
		//需要显示转换,因为Dollar没有implicit转换成Rmb
		var krwOfHong = (Krw)hong.Money;
		Console.WriteLine($"小红有{hong.Money.DollarAmount}美元,换成韩元是{krwOfHong.KrwAmount}元");

		#endregion

		#region 小黑,韩元换人民币

		Console.WriteLine("小黑只有韩元1000元,但是出国换货币的时候,都是央行的汇率,所以她的韩元要先转换成美元,然后再转换成其他货币");

		var krwOfHei = new Krw
		{
			KrwAmount = 1000
		};
		var hei = new Person
		{
			Name = "小黑",
			// 不需要显示转换,因为Krw有implicit转换成Dollar
			Money = krwOfHei
		};

		//需要显示转换,因为Dollar没有implicit转换成Rmb
		var rmbOfHei = (Rmb)hei.Money;
		Console.WriteLine($"小黑有{hei.Money.DollarAmount}美元,换成人民币是{rmbOfHei.RmbAmount}元");

		#endregion

		#region 统一用Dollar来表示资产

		Console.WriteLine($"小明有{ming.Money.DollarAmount}美元");
		Console.WriteLine($"小红有{hong.Money.DollarAmount}美元");
		Console.WriteLine($"小黑有{hei.Money.DollarAmount}美元");

		#endregion

		#region 统一用Rmb来表示资产

		Console.WriteLine($"小明有{((Rmb)ming.Money).RmbAmount}人民币");
		Console.WriteLine($"小红有{((Rmb)hong.Money).RmbAmount}人民币");
		Console.WriteLine($"小黑有{((Rmb)hei.Money).RmbAmount}人民币");

		#endregion

		#region 统一用Krw来表示资产

		Console.WriteLine($"小明有{((Krw)ming.Money).KrwAmount}韩元");
		Console.WriteLine($"小红有{((Krw)hong.Money).KrwAmount}韩元");
		Console.WriteLine($"小黑有{((Krw)hei.Money).KrwAmount}韩元");

		#endregion
	}
}

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

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

相关文章

HCIA-HarmonyOS设备开发认证V2.0-轻量系统内核基础-事件event

目录 一、事件基本概念二、事件运行机制三、事件开发流程四、事件使用说明五、事件接口坚持就有收获 一、事件基本概念 事件是一种实现任务间通信的机制&#xff0c;可用于实现任务间的同步&#xff0c;但事件通信只能是事件类型的通信&#xff0c;无数据传输。一个任务可以等…

LeetCode、452. 用最少数量的箭引爆气球【中等,贪心,区间问题】

文章目录 前言LeetCode、452. 用最少数量的箭引爆气球【中等&#xff0c;贪心&#xff0c;区间问题】题目链接与分类思路贪心&#xff0c;连续区间数量问题 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客…

带你掌握getchar与putchar的基本用法

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 目录 getcahr putchar getchar 与 putchar 的配合使用 getchar相较于scanf的优缺点 putchar相较于printf的优缺点 getcahr 函数原型&#xff1a…

【教程】MySQL数据库学习笔记(二)——数据类型(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 文章目录 【MySQL数据库学习】系列文章一、整…

DFM-无监督图像匹配

DFM&#xff1a;A Performance Baseline for Deep Feature Matching&#xff08;深度特征匹配的性能基准&#xff09; 2021.06.14 摘要 提出了一种新的图像匹配方法&#xff0c;利用现成的深度神经网络提取的学习特征来获得良好的图像匹配效果。该方法使用预训练的VGG结构作为…

starknet之 class_hash

文章目录 问题背景什么是Class Hash问题背景 部署合约报错:ReferenceError: Buffer is not defined 什么是Class Hash 官方: https://book.starknet.io/ch04-03-01-deploy-standard-account.html?highlight=class%20hash#finding-the-class-hash 要部署智能合约,您需要在…

【原创 附源码】Flutter集成Apple支付详细流程(附源码)

最近有时间&#xff0c;特意整理了一下之前使用过的Flutter平台的海外支付&#xff0c;附源码及demo可供参考 这篇文章只记录Apple支付的详细流程&#xff0c;其他相关Flutter文章链接如下&#xff1a; 【原创 附源码】Flutter集成谷歌支付详细流程(附源码) 【原创 附源码】F…

PR:熟悉PR工作环境

新建项目 设置自己的页面布局 首选项

【JavaEE】_JavaScript基础语法

目录 1. JavaScript概述 1.1 JavaScript简介 1.2 HTML、CSS、JavaScript的关系 1.3 JavaScrip的组成 2. JavaScript的书写形式 2.1 内嵌式 2.2 行内式 2.3 外部式 3. 输出 3.1 alert 3.2 console.log 4. 变量的使用 4.1 创建变量 4.1.1 使用var 4.1.2 使用let …

java中事务的使用

文章目录 前言一、同一张表1.业务代码2.测试代码3.测试结果 二、不同表1.业务代码2.测试代码3.测试结果 总结 前言 本文将介绍在springboot中使用Transactional注解来完成对数据库事务的操作&#xff0c;保证数据一致性。 一、同一张表 1.业务代码 Controller Controller p…

停止内耗,做有用的事

很多读者朋友跟我交流的时候&#xff0c;都以为我有存稿&#xff0c;于是听到我说每周四现写的时候都很惊讶。其实没什么好惊讶的&#xff0c;每周四我都会把自己关在书房里一整天&#xff0c;断掉一切电话、微信、邮件&#xff0c;从中午写到晚上&#xff0c;直到写完为止。 这…

算法学习——LeetCode力扣回溯篇1

算法学习——LeetCode力扣回溯篇1 77. 组合 77. 组合 - 力扣&#xff08;LeetCode&#xff09; 描述 任何顺序 返回答案。 示例 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 示例 2&#xff1a; 输…

springboot743二手交易平台

springboot743二手交易平台 获取源码——》公主号&#xff1a;计算机专业毕设大全

《Java 简易速速上手小册》第8章:Java 性能优化(2024 最新版)

文章目录 8.1 性能评估工具 - 你的性能探测仪8.1.1 基础知识8.1.2 重点案例&#xff1a;使用 VisualVM 监控应用性能8.1.3 拓展案例 1&#xff1a;使用 JProfiler 分析内存泄漏8.1.4 拓展案例 2&#xff1a;使用 Gatling 进行 Web 应用压力测试 8.2 JVM 调优 - 魔法引擎的调校8…

第四篇【传奇开心果微博系列】Python微项目技术点案例示例:美女颜值判官

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展思路四、添加不同类型的美女示例代码五、增加难度等级示例代码六、添加特殊道具示例代码七、设计关卡系统示例代码八、添加音效和背景音乐示例代码九、多人游戏…

【解决】idea控制台不输出trace/debug日志

idea控制台不输出trace日志 问题原因解决 问题 idea控制台不输出trace日志。 pom文件&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency>输出lo…

23种计模式之Python/Go实现

目录 设计模式what?why?设计模式&#xff1a;设计模式也衍生出了很多的新的种类&#xff0c;不局限于这23种创建类设计模式&#xff08;5种&#xff09;结构类设计模式&#xff08;7种&#xff09;行为类设计模式&#xff08;11种&#xff09; 六大设计原则开闭原则里氏替换原…

P3612 [USACO17JAN] Secret Cow Code S题解

题目 奶牛正在试验秘密代码&#xff0c;并设计了一种方法来创建一个无限长的字符串作为其代码的一部分使用。 给定一个字符串&#xff0c;让后面的字符旋转一次&#xff08;每一次正确的旋转&#xff0c;最后一个字符都会成为新的第一个字符&#xff09;。也就是说&#xff0…

localStorage、sessionStorage、cookie区别

localStorage: localStorage 的生命周期是永久的&#xff0c;关闭页面或浏览器之后 localStorage 中的数据也不会消失。localStorage 除非主动删除数据&#xff0c;否则数据永远不会消失 sessionStorage: sessionStorage 的生命周期是仅在当前会话下有效。sessionStorage 引入…

【STM32 CubeMX】STM32中断体系结构

文章目录 前言一、中断体系的比喻二、中断的内部结构2.1 EXTI触发方式 2.2 NVIC2.3 cpu与中断2.4 外部中断控制器框图上升沿触发选择寄存器屏蔽/使能寄存器等待处理寄存器 2.5 中断优先级 总结 前言 一、中断体系的比喻 STM32中断体系如下图所示&#xff1a; 一座大型建筑物…