c#编译时提高兼容性

介绍

使用幻像类型是一种非常简单的技术,可用于提高代码的编译时安全性。 有很多潜在的用例,其复杂性程度各不相同,但是即使幻像类型的轻量级使用也可以显着提高编译时的安全性。 幻像类型只是带有未使用类型参数的参数化类型。 例如:

public class MyPhantomType<T> {public String sayHello() {return 'hello';}// other methods/fields that never refer to T
}

该示例类的类型参数为T,但实际上从未在代码中使用。 乍一看,这似乎没有什么用,但事实并非如此! 幻像类型的所有对象实例都带有类型信息,因此该技术可用于“标记”带有一些可在编译时检查的额外信息的值。 当然,我们可以通过编写不带泛型的代码来随时逃避键入操作,但是应该不惜一切代价避免这种情况。 某些语言(例如Scala)完全不允许删除类型参数,因此使用Scala时,您将始终必须完全保留类型信息。

示例用例和实现

幻像类型最简单,最有用的用例之一是数据库ID。 如果我们有一个典型的三层(数据,服务,Web)Java Web应用程序,则可以通过在架构的“端点”以外的任何地方用幻像类型替换原始id来获得大量的编译时安全性。 因此,数据层会将原始ID放入数据库查询中,而Web层可能会从外部资源(例如HTTP参数)获取原始ID,但是否则,我们始终会处理幻像类型。 在此示例中,我假设数据库ID类型始终为64位长数字。 首先,我们需要将由所有“实体类”实现的标记器接口,该接口应受幻像类型id机制支持:

public interface Entity {Long getId();
}

该标记接口的唯一目的是将我们的幻像型id限制为一组标记的类,并提供将在实现中使用的getId方法。 实际的幻像类型是单个id值的不可变容器。 type参数表示id的“目标类型”,这使得可以以编译时安全的方式在不同实体的id值之间进行区分。 我喜欢将此类称为Ref(参考的简写),但这只是个人选择。

@Value
@RequiredArgsConstructor(AccessLevel.PRIVATE)
public final class Ref<T extends Entity> implements Serializable {public final long id;  public static <T extends Entity> Ref<T> of(T value) {return new Ref<T>(value.getId());}public static <T extends Entity> Ref<T> of(long id, Class<T> clazz) {return new Ref<T>(id);}@Overridepublic String toString() {return String.valueOf(id);}}

此示例类使用Project Lombok中的@Value和@RequiredArgsConstructor批注。 如果您不使用Lombok,请手动添加构造函数,getter,equals和hashCode实现(或在下面查找完整的实现)。 请注意,类型参数T永远不会在任何地方使用。 这也意味着您在运行时无法知道Ref的类型,但这通常不是必需的。

使用示例实现

现在,我们将在可能的情况下将原始ID替换为Refs。 例如,我们可以有一个将用户添加到组中的服务级别方法:

void addUserToGroup(long userId, long groupId);
// without parameter names
void addUserToGroup(long, long);// VSvoid addUserToGroup(Ref<User> userRef, Ref<Group> groupRef);
// without parameter names
void addUserToGroup(Ref<User>, Ref<Group>);

现在,当我们要调用此方法时,将始终需要Ref对象而不是原始的long值。 在此示例中,有两种获取参考值的方法。

  1. 如果您有实际对象的实例,请调用Ref.of(object)。 这是除Web以外的其他层中最常见的方法
  2. 如果您有原始ID,并且知道目标类型,请调用Ref.of(id,TargetType.class)。 如果原始ID来自外部,则通常在Web层中需要这样做

为了从Ref中提取原始ID值,您可以阅读该字段或使用吸气剂。 通常仅在数据库查询构建之前才需要这样做。

总结思想

为了了解裁判的好处,请尝试考虑以下情况:

  • 如果您在采用不同类型ID的方法调用中更改参数顺序,会发生什么情况? (例如我们的addUserToGroup)
  • 如果更改数据库ID的类型(例如Integer-> Long或Long-> UUID)会发生什么?
  • 如果您经常具有与id相同类型但不是id的方法参数,那么您将有多大可能出现运行时错误? 例如,如果您具有整数ID,并且在同一方法中混合了ID和某种列表索引

在所有这些情况下,使用Refs都可以确保在代码不正确的地方出现编译时错误。 在典型的代码库中,这是不费吹灰之力的巨大胜利。 编译时的安全性降低了重构的成本和难度,这使得维护代码库变得非常容易和安全。

数据库ID只是幻像类型的简单示例。 其他典型用例包括某种状态机(例如,Order <InProcess>,Order <Completed>与仅Order对象),以及某种类型的值单位信息(例如,LongNumber <Weight>,LongNumber <Temperature>与longs) 。

Ref <T>实现(无Lombok)

public final class Ref<T extends Entity> implements Serializable {public final long id;public static <T extends Entity> Ref<T> of(T value) {return new Ref<T>(value.getId());}public static <T extends Entity> Ref<T> of(long id, Class<T> clazz) {return new Ref<T>(id);}@Overridepublic String toString() {return String.valueOf(id);}private Ref(long id) {this.id = id;}public long getId() {return this.id;}@Overridepublic int hashCode() {return (int) (id ^ (id >>> 32));}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || o.getClass() != this.getClass())return false;Ref<?> other = (Ref<?>) o;return other.id == this.id;}
}

参考: Gekkio的技术博客博客中的JCG合作伙伴 Joonas Javanainen提出了幻像类型 , 提高了编译时安全性 。

翻译自: https://www.javacodegeeks.com/2013/02/increased-compile-time-safety-with-phantom-types.html

c#编译时提高兼容性

c#编译时提高兼容性_幻像类型提高了编译时的安全性相关推荐

  1. 佩伯尔幻像_幻像类型提高了编译时的安全性

    佩伯尔幻像 介绍 使用幻像类型是一种非常简单的技术,可用于提高代码的编译时安全性. 有很多潜在的用例具有不同的复杂性级别,但是即使幻像类型的轻量级使用也可以显着提高编译时的安全性. 幻像类型只是带有未 ...

  2. 幻像类型提高了编译时的安全性

    介绍 使用幻像类型是一种非常简单的技术,可用于提高代码的编译时安全性. 有许多潜在的用例具有不同的复杂性级别,但是即使幻像类型的使用非常轻巧,也可以显着提高编译时的安全性. 幻像类型只是带有未使用类型 ...

  3. 预编译sql查询语句_频繁的查询重新编译– SQL查询性能的杀手–简介

    预编译sql查询语句 In this article, we will explain what compilations and recompilations are, and give recom ...

  4. 微信小程序反编译wxss文件缺失_微信小程序反编译~2020年

    摘要 安装wxappUnpacker小程序反编译工具并使用(2020.03) 关键词: 微信小程序反编译 wxss 介绍 上次分享了web前端爬取工具 ,那么这次也同样讲讲微信小程序反编译吧,对于像博 ...

  5. mysql编译安装指定端口_在CentOS7系统上编译安装MySQL 5.7.13步骤详解

    MySQL 5.7主要特性 1.更好的性能 对于多核CPU.固态硬盘.锁有着更好的优化,每秒100W QPS已不再是MySQL的追求,下个版本能否上200W QPS才是用户更关心的. 2.更好的Inn ...

  6. 自定义拍照时 拍照界面_在用透射电镜拍照时为什么经常要插入物镜光阑?

    在用透射电镜给样品尤其是多晶样品拍照时,经常会插入物镜光阑,这是什么原因呢?为了解释这个问题,我们还得用到衍射和衬度传递函数(Contrast Transfer Function)的知识,关于衬度传递 ...

  7. java反编译微信小程序_微信小程序反编译的实现

    首先声明:本文章仅供学习之用,不可它用. 一.前言 看到人家上线的小程序的效果,纯靠推测,部分效果在绞尽脑汁后能做出大致的实现,但是有些细节,费劲全力都没能做出来.很想一窥源码?查看究竟?看看大厂的前 ...

  8. java date 不要时分秒_java 去掉Date类型 年月日 后面的时分秒

    因为用到这个东西了,随手查了下,发现解决方案有很多,作为一个菜鸟来说 有必要整理一下-.- 我用的是hibernate自己生成的表,所以util Date在mysql  只有年月日 而没有时分秒 然后 ...

  9. mysql提高吞吐量_垃圾收集:提高吞吐量

    mysql提高吞吐量 这篇文章的灵感来自于在内存管理术语表中碰到" Pig in the Python "的定义之后. 显然,该术语用于解释GC反复促进大对象世代相传的情况. 据推 ...

最新文章

  1. B 站 Up 主自制秃头生成器,独秃头不如众秃头?
  2. xx is not in the sudoers file 问题解决
  3. centos7 ifconfig命令找不到_分享一个解决 sudo 命令找不到环境变量的小技巧
  4. 腾讯开源之道:基于Apache之道的开源实践与探索
  5. 3 描述android的组件,Android基础------Intent组件
  6. 编写绘图代码的技巧(二)
  7. c语言中调用平均成绩,C语言、用调用函数、输入3个学生5门课程的成绩分别用函数求每个学生平均分每门课的平均分...
  8. CSR8675项目实战:BlueAg蓝牙一拖二发射器
  9. 适合程序员学习的国外网站推荐
  10. 面试官最不喜欢不认同的5个跳槽理由
  11. 32个高效思维模型,快速提升你的思考力!
  12. Bill Gates和Elon Musk推荐,人工智能必读的三本书 -《终极算法》,《超级智能》和《终极发明》
  13. java无响应_Java HttpClient请求无响应解决方案
  14. c语言快排过程,快速排序(快排)C语言实现
  15. MBA-day17 假言推理:如果的考法与题型
  16. Unity 3分钟,将你的Terrin 地形转为FBX
  17. 国资分拆上市第一股!上海电气风电集团正式募股上市
  18. Elasticsearch Ingest Pipeline
  19. 文件名和文件夹的bat批量重命名替换关键字和删除关键字
  20. js:如何监听history的pushState方法和replaceState方法。(高阶函数封装+自定义事件)

热门文章

  1. Mysql中的行级锁、表级锁、页级锁
  2. Jodd - Java界的瑞士军刀轻量级工具包
  3. C/C++输入输出流
  4. “老师,我不要苹果味的,我要葡萄味的”!
  5. 150. 逆波兰表达式求值---JAVA---LeetCode
  6. foxmail 不知道这样的主机_华为P50真机图!网友:早知道这样,就不加价买mate40了...
  7. (转)threadPoolExecutor 中的 shutdown() 、 shutdownNow() 、 awaitTermination() 的用法和区别
  8. 算法运行时间中的对数
  9. GET与POST传递数据的最大长度能够达到多少
  10. cognito_将Spring Boot应用程序与Amazon Cognito集成