<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
System.Tuple
型別是在.NET 4.0中引入的,但是有兩個明顯的缺點:
(1) Tuple 型別是參照型別。
(2) 沒有建構函式支援。
為了解決這些問題,C# 7 引入了新的語言功能以及新的型別。
現在,如果您需要從函數中返回兩個值的合併結果,或者把兩個值合併到一個雜湊表中,可以使用System.ValueTuple
型別並使用一個精短的語法來構造它們:
// 構建元組範例 var tpl = (1, 2); // 在字典中使用元組 var d = new Dictionary<(int x, int y), (byte a, short b)>(); // 不同名稱的元組是相容的 d.Add(tpl, (a: 3, b: 4)); // 元組值的語意 if (d.TryGetValue((1, 2), out var r)) { // 解構元組忽略第一個元素 var (_, b) = r; // 使用命名語法和定義名稱 Console.WriteLine($"a: {r.a}, b: {r.Item2}"); }
System.ValueTuple
型別在.NET Framework 4.7中引入。但是您仍然可以在較低的框架版本中使用這個功能,這時候,您必須參照一個特殊的nuget包:System.ValueTuple。
(Type1 name1, Type2 name2)
。(value1, optionalName: value2)
。(int a, int b) = (1, 2)
。(1,2).Equals((a: 1, b: 2))
、(1,2).GetHashCode() == (1,2).GetHashCode()
返回的值均是true
。==
和!=
。在github上有一個懸而未決的討論:“支援==和!=元組型別”。case
語句中轉換:var (x, y) = (1,2)
- OK, (var x, int y) = (1,2)
- OK, dictionary.TryGetValue(key, out var (x, y))
- not OK, case var (x, y): break;
- not OK。(int a, int b) x = (1,2); x.a++;
.Item1
、Item2
等來存取。我們馬上就會明白上面幾點。
缺少使用者定義的名稱導致System.Tuple
型別不常用。我們可以將System.Tuple
用作一個精減方法的實現細節,但如果我們需要傳遞它,我更喜歡使用具有描述性屬性名稱的命名型別。新元組功能很好地解決了這個問題:可以為元組元素指定名稱,而不像匿名型別,即使在不同的程式集中也可以使用這些名稱。
C#編譯器為方法簽名中使用的每個元組型別指定了一個特殊的標記TupleElementNamesAttribute
:
TupleElementNamesAttribute
標記非常特殊,不能在使用者程式碼中直接使用。如果您嘗試使用它,編譯器會報出錯誤。
public (int a, int b) Foo1((int c, int d) a) => a; [return: TupleElementNames(new[] { "a", "b" })] public ValueTuple<int, int> Foo( [TupleElementNames(new[] { "c", "d" })] ValueTuple<int, int> a) { return a; }
這有助於IDE和編譯器“檢查”元素名稱,並警告錯誤地使用它們:
// 正確: 元組宣告可以跳過元素名稱 (int x, int y) tpl = (1, 2); // 警告: 由於目標型別「(int x, int y)」指定了其他名稱或未指定名稱,因此元組元素名稱「a」被忽略。 tpl = (a:1, b:2); // 正確 :元組解構忽略元素名稱 var (a, b) = tpl; // x: 2, y: 1. 元組名被忽略 var (y, x) = tpl;
編譯器對繼承的成員有較強的要求:
public abstract class Base { public abstract (int a, int b) Foo(); public abstract (int, int) Bar(); } public class Derived : Base { // 錯誤:替代繼承成員「Base.Foo()」時無法更改元組元素名稱 public override (int c, int d) Foo() => (1, 2); // 錯誤:替代繼承成員「Base.Bar()」時無法更改元組元素名稱 public override (int a, int b) Bar() => (1, 2); }
常規方法引數可以在重寫成員中自由更改,重寫成員中的元組元素名稱應該與基本型別中的元素名稱完全匹配。
C# 7.1 引入了一個額外的增強功能:元素名稱推斷類似於C#為匿名型別所做的推斷。
public void NameInference(int x, int y) { // (int x, int y) var tpl = (x, y); var a = new {X = x, Y = y}; // (int X, int Y) var tpl2 = (a.X, a.Y); }
元組是公共欄位可變的值型別。這聽起來令人擔憂,因為我們知道可變值型別被認為是有害的。這是一個邪惡的小例子:
var x = new { Items = new List<int> { 1, 2, 3 }.GetEnumerator() }; while (x.Items.MoveNext()) { Console.WriteLine(x.Items.Current); }
如果執行這個程式碼,您會得到一個無限迴圈。List<T>.Enumerator
是一個可變值型別,但是Items
是屬性。這意味著x.Items
在每個迴圈迭代中返回原始迭代器的副本,從而導致無限迴圈。
但是隻有當資料與行為混合在一起時,可變值型別才是危險的:列舉元擁有一個狀態(當前元素)並具有行為(通過呼叫MoveNext方法來推進迭代器的能力)。這種組合可能會導致問題,因為在副本上呼叫方法而不是在原始範例上呼叫方法,從而導致無效操作。下面是一組由於值型別的隱藏副本而導致不明顯行為的範例:gist。
但可變性問題依然存在:
var tpl = (x: 1, y: 2); var hs = new HashSet<(int x, int y)>(); hs.Add(tpl); tpl.x++; Console.WriteLine(hs.Contains(tpl)); // false
元組在字典中作為鍵是非常有用的,並且由於適當的值語意可以儲存在雜湊表中。但是您不應該在集合的不同操作之間改變一個元組變數的狀態。
雖然元組的建構函式對於元組來說非常特殊的,但是解構非常通用,並且可以與任何型別一起使用。
public static class VersionDeconstrucion { public static void Deconstruct(this Version v, out int major, out int minor, out int build, out int revision) { major = v.Major; minor = v.Minor; build = v.Build; revision = v.Revision; } } var version = Version.Parse("1.2.3.4"); var (major, minor, build, _) = version; // Prints: 1.2.3 Console.WriteLine($"{major}.{minor}.{build}");
解構使用“鴨子型別(duck-typing)”的方法:如果編譯器可以找到一個方法呼叫Deconstruct
給定的型別 - 實體方法或擴充套件方法 - 型別即是可解構的。
一旦您開始使用元組,很快就會意識到想在原始碼的多個地方“重用”一個元組型別,但這並沒有什麼問題。首先,雖然C#不支援給定型別的全域性別名,不過您可以使用“using”別名指令,它會在一個檔案中建立一個別名;其次,您不能將元組指定別名:
//您不能這樣做:編譯錯誤 using Point = (int x, int y); // 但是您可以這樣做 using SetOfPoints = System.Collections.Generic.HashSet<(int x, int y)>;
github上有一個關於“使用指令中的元組型別”的討論。所以,如果您發現自己在多個地方使用一個元組型別,你有兩個選擇:保持複製貼上或建立一個命名的型別。
下面是一個有趣的問題:我們應該遵循什麼命名規則來處理元組元素?Pascal規則喜歡ElementName
還是駱峰規則elementName
?一方面,元組元素應該遵循公共成員的命名規則(即PascalCase),但另一方面,元組只是包含變數的變數,變數應該遵循駱峰規則。
如果元組被用作引數或方法的返回型別使用PascalCase
規則,並且如果在函數中本地建立元組使用camelCase
規則,可以考慮使用基於用法和使用的不同命名方案。但我更喜歡總是使用camelCase
。
我發現元組在日常工作中非常有用。我需要不止一個函數返回值,或者我需要把一對值放入一個雜湊表,或者字典的Key非常複雜,我需要用另一個“欄位”來擴充套件它。
我甚至使用它們來避免與方法類似的ConcurrentDictionary.TryGetOrAdd
的閉包分配,需要額外的引數。在許多情況下,狀態也是一個元組。
該功能是非常有用的,但我還想看到一些增強功能:
out var
、case var
語法。==
進行相等比較。到此這篇關於C#元組型別ValueTuple用法詳解的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45