c#编译时提高兼容性_幻像类型提高了编译时的安全性
c#編譯時提高兼容性
介紹
使用幻像類型是一種非常簡單的技術,可用于提高代碼的編譯時安全性。 有很多潛在的用例,其復雜性程度各不相同,但是即使幻像類型的輕量級使用也可以顯著提高編譯時的安全性。 幻像類型只是帶有未使用類型參數的參數化類型。 例如:
該示例類的類型參數為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值。 在此示例中,有兩種獲取參考值的方法。
為了從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#编译时提高兼容性_幻像类型提高了编译时的安全性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何脚踏实地构建Java Agent
- 下一篇: 医院HIS系统简介