มีวิธีที่ดีกว่าในการใช้พจนานุกรม C # มากกว่า TryGetValue หรือไม่


19

ฉันพบว่าตัวเองมักจะค้นหาคำถามออนไลน์และวิธีแก้ปัญหาหลายอย่างรวมถึงพจนานุกรม อย่างไรก็ตามเมื่อใดก็ตามที่ฉันพยายามที่จะใช้พวกเขาฉันได้กลิ่นที่น่ากลัวนี้ในรหัสของฉัน เช่นทุกครั้งที่ฉันต้องการใช้ค่า:

int x;
if (dict.TryGetValue("key", out x)) {
    DoSomethingWith(x);
}

นั่นคือโค้ด 4 บรรทัดที่จะทำสิ่งต่อไปนี้: DoSomethingWith(dict["key"])

ฉันได้ยินมาว่าการใช้คีย์เวิร์ด out เป็นรูปแบบการต่อต้านเพราะทำให้ฟังก์ชั่นกลายพันธุ์พารามิเตอร์ของพวกเขา

นอกจากนี้ฉันพบว่าตัวเองมักต้องการพจนานุกรม "กลับด้าน" ที่ฉันพลิกคีย์และค่าต่างๆ

ในทำนองเดียวกันฉันมักจะทำซ้ำผ่านรายการในพจนานุกรมและพบว่าตนเองกำลังแปลงคีย์หรือค่าไปยังรายการ ฯลฯ เพื่อทำสิ่งนี้ให้ดีขึ้น

ฉันรู้สึกว่ามีวิธีการใช้พจนานุกรมที่ดีกว่าและสง่างามกว่าอยู่เกือบตลอดเวลา แต่ฉันรู้สึกแย่


7
อาจมีวิธีอื่นอยู่บ้าง แต่โดยทั่วไปฉันจะใช้containKeyก่อนที่จะลองรับค่า
Robbie Dee

2
จริงๆแล้วมีข้อยกเว้นบางประการถ้าคุณรู้ว่ากุญแจพจนานุกรมของคุณอยู่ตรงไหนก่อนหน้านั่นอาจเป็นวิธีที่ดีกว่า ถ้าคุณไม่รู้ปุ่ม dict ไม่ควรเขียนรหัสแบบยากเลย พจนานุกรมมีประโยชน์สำหรับการทำงานกับวัตถุกึ่งโครงสร้างหรือข้อมูลซึ่งฟิลด์นั้นมีมุมฉากเป็นส่วนใหญ่ในการประยุกต์ใช้ IMO ยิ่งเขตข้อมูลเหล่านั้นใกล้เคียงกับแนวคิดธุรกิจ / โดเมนที่เกี่ยวข้องมากขึ้นพจนานุกรมที่มีประโยชน์น้อยกว่าจะกลายเป็นสิ่งที่ทำงานกับแนวคิดเหล่านั้น
svidgen

6
@ RobbieDee: คุณต้องระวังเมื่อคุณทำเช่นนั้นเพราะการทำเช่นนั้นจะสร้างสภาพการแข่งขัน อาจเป็นไปได้ที่คีย์นั้นจะถูกลบออกระหว่างการโทรที่มีKeyKeyและรับค่า
whatsisname

12
@whatsisname: ภายใต้เงื่อนไขเหล่านั้นConcurrentDictionaryจะเหมาะสมกว่า คอลเลกชันในSystem.Collections.Genericnamespace ไม่ได้ด้ายปลอดภัย
Robert Harvey

4
ดี 50% ของเวลาที่ฉันเห็นคนใช้Dictionaryสิ่งที่พวกเขาต้องการจริง ๆ เป็นคลาสใหม่ สำหรับ 50% (เท่านั้น) มันเป็นกลิ่นที่ออกแบบ
BlueRaja - Danny Pflughoeft

คำตอบ:


23

พจนานุกรม (C # หรืออย่างอื่น) เป็นเพียงคอนเทนเนอร์ที่คุณค้นหาค่าโดยใช้คีย์ ในหลายภาษาจะมีการระบุอย่างถูกต้องว่าเป็นแผนที่ซึ่งมีการใช้งานทั่วไปเป็น HashMap

ปัญหาที่ต้องพิจารณาคือสิ่งที่เกิดขึ้นเมื่อไม่มีคีย์ บางภาษาทำงานโดยการส่งคืนnullหรือnilค่าที่เทียบเท่าอื่น ๆ การเริ่มต้นที่เงียบ ๆ เป็นค่าแทนที่จะแจ้งให้คุณทราบว่าไม่มีค่าอยู่

ไม่ว่าจะดีขึ้นหรือแย่ลงนักออกแบบห้องสมุด C # ก็มีสำนวนเพื่อจัดการกับพฤติกรรม พวกเขาให้เหตุผลว่าพฤติกรรมเริ่มต้นสำหรับการค้นหาค่าที่ไม่มีอยู่คือการโยนข้อยกเว้น หากคุณต้องการหลีกเลี่ยงข้อยกเว้นคุณสามารถใช้Tryตัวแปรได้ มันเป็นวิธีการเดียวกับที่ใช้ในการแยกสตริงเป็นจำนวนเต็มหรือวัตถุวันที่ / เวลา โดยพื้นฐานแล้วผลกระทบจะเป็นดังนี้:

T count = int.Parse("12T45"); // throws exception

if (int.TryParse("12T45", out count))
{
    // Does not throw exception
}

และนำไปสู่พจนานุกรมซึ่งผู้ทำดัชนีมอบหมายให้Get(index):

var myvalue = dict["12345"]; // throws exception
myvalue = dict.Get("12345"); // throws exception

if (dict.TryGet("12345", out myvalue))
{
    // Does not throw exception
}

นี่เป็นเพียงวิธีการออกแบบภาษา


ควรoutลดทอนตัวแปรหรือไม่

C # ไม่ใช่ภาษาแรกที่มีและพวกเขามีจุดประสงค์ในสถานการณ์เฉพาะ หากคุณพยายามสร้างระบบที่เกิดขึ้นพร้อมกันอย่างมากคุณจะไม่สามารถใช้outตัวแปรที่ขอบเขตการทำงานพร้อมกันได้

ในหลาย ๆ ทางหากมีสำนวนที่ดำเนินการโดยผู้ให้บริการภาษาและห้องสมุดหลักฉันพยายามที่จะใช้สำนวนเหล่านั้นใน API ของฉัน นั่นทำให้ API รู้สึกสอดคล้องกันมากขึ้นและเหมือนอยู่บ้านในภาษานั้น ดังนั้นวิธีที่เขียนใน Ruby จะไม่ดูเหมือนวิธีที่เขียนใน C #, C หรือ Python พวกเขาแต่ละคนมีวิธีการสร้างรหัสที่ต้องการและการทำงานกับสิ่งนั้นจะช่วยให้ผู้ใช้ API ของคุณเรียนรู้ได้เร็วขึ้น


แผนที่โดยทั่วไปเป็นแบบป้องกันหรือไม่

พวกเขามีจุดประสงค์ของพวกเขา แต่หลายครั้งพวกเขาอาจเป็นทางออกที่ผิดสำหรับจุดประสงค์ที่คุณมี โดยเฉพาะถ้าคุณมีการทำแผนที่สองทิศทางที่คุณต้องการ มีคอนเทนเนอร์และวิธีจัดระเบียบข้อมูลมากมาย มีวิธีการมากมายที่คุณสามารถใช้ได้และบางครั้งคุณต้องคิดให้ดีก่อนที่จะเลือกคอนเทนเนอร์นั้น

หากคุณมีรายการการแมปสองทิศทางที่สั้นมากคุณอาจต้องการเพียงรายการของสิ่งอันดับ หรือรายการของโครงสร้างที่คุณสามารถค้นหาคู่แรกได้อย่างง่ายดายทั้งสองด้านของการจับคู่

นึกถึงโดเมนปัญหาและเลือกเครื่องมือที่เหมาะสมที่สุดสำหรับงาน หากไม่มีอยู่ให้สร้างขึ้นมา


4
"จากนั้นคุณอาจต้องการเพียงรายการของ tuples หรือรายการของ structs ซึ่งคุณสามารถค้นหาคู่แรกได้อย่างง่ายดายทั้งสองด้านของการแมป" - หากมีการปรับแต่งสำหรับชุดเล็ก ๆ ควรทำในไลบรารีไม่ใช่ประทับยางลงในรหัสผู้ใช้
Blrfl

หากคุณเลือกรายการของสิ่งอันดับหรือพจนานุกรมนั่นคือรายละเอียดการนำไปใช้งาน มันเป็นคำถามของการเข้าใจโดเมนปัญหาของคุณและใช้เครื่องมือที่เหมาะสมสำหรับงาน เห็นได้ชัดว่ามีรายการอยู่และมีพจนานุกรมอยู่ สำหรับกรณีทั่วไปพจนานุกรมถูกต้อง แต่อาจเป็นหนึ่งหรือสองแอพพลิเคชั่นที่คุณต้องใช้รายการ
Berin Loritsch

3
มันเป็น แต่ถ้าพฤติกรรมยังคงเหมือนเดิมการตัดสินใจนั้นควรอยู่ในห้องสมุด ฉันทำงานข้ามการใช้งานคอนเทนเนอร์ในภาษาอื่น ๆ ที่จะเปลี่ยนอัลกอริทึมถ้าบอกเบื้องต้นว่าจำนวนรายการจะมีขนาดเล็ก ฉันเห็นด้วยกับคำตอบของคุณ แต่เมื่อคิดออกแล้วมันควรจะอยู่ในห้องสมุดบางทีอาจเป็น SmallBidirectionalMap
Blrfl

5
"เพื่อให้ดีขึ้นหรือแย่ลงผู้ออกแบบห้องสมุด C # ขึ้นมาพร้อมกับสำนวนเพื่อจัดการกับพฤติกรรม" - ฉันคิดว่าคุณโดนตะปูหัว: พวกเขา "ขึ้นมา" ด้วยสำนวนแทนที่จะใช้ที่ใช้กันอย่างแพร่หลายที่มีอยู่แล้วซึ่งเป็นประเภทผลตอบแทนเสริม
Jörg W Mittag

3
@ JörgWMittagถ้าคุณกำลังพูดถึงสิ่งที่ชอบOption<T>ฉันคิดว่ามันจะทำให้วิธีการใช้ใน C # 2.0 ยากขึ้นเพราะมันไม่มีการจับคู่รูปแบบ
svick

21

คำตอบที่ดีบางประการเกี่ยวกับหลักการทั่วไปของแฮชเทเบิล / พจนานุกรม แต่ฉันคิดว่าฉันจะสัมผัสกับตัวอย่างรหัสของคุณ

int x;
if (dict.TryGetValue("key", out x)) 
{
    DoSomethingWith(x);
}

ในฐานะของ C # 7 (ซึ่งฉันคิดว่าอายุประมาณสองปี) ที่สามารถทำให้ง่ายต่อการ:

if (dict.TryGetValue("key", out var x))
{
    DoSomethingWith(x);
}

และแน่นอนว่ามันสามารถลดลงเป็นหนึ่งบรรทัด:

if (dict.TryGetValue("key", out var x)) DoSomethingWith(x);

หากคุณมีค่าเริ่มต้นเมื่อไม่มีคีย์อยู่อาจเป็น:

DoSomethingWith(dict.TryGetValue("key", out var x) ? x : defaultValue);

ดังนั้นคุณสามารถบรรลุรูปแบบที่กะทัดรัดได้โดยใช้การเพิ่มภาษาล่าสุดอย่างสมเหตุสมผล


1
เรียกใช้ไวยากรณ์ v7 ได้ดีตัวเลือกเล็ก ๆ น้อย ๆ ที่ดีต้องตัดออกบรรทัดคำจำกัดความพิเศษในขณะที่อนุญาตให้ใช้ var +1
BrianH

2
โปรดทราบด้วยว่าหาก"key"ไม่มีอยู่xจะเริ่มต้นได้default(TValue)
Peter Duniho

อาจจะดีในฐานะส่วนขยายทั่วไปด้วยเช่นกัน"key".DoSomethingWithThis()
Ross Presser

ฉันชอบการออกแบบที่ใช้getOrElse("key", defaultValue) วัตถุ Nullเป็นอย่างมากยังคงเป็นรูปแบบที่ฉันโปรดปราน ทำงานอย่างนั้นและคุณไม่สนใจว่าTryGetValueจะคืนจริงหรือเท็จ
candied_orange

1
ฉันรู้สึกว่าคำตอบนี้สมควรได้รับการปฏิเสธว่าการเขียนโค้ดขนาดกะทัดรัดเพื่อประโยชน์ของการกระชับข้อมูลนั้นไม่ดี มันทำให้โค้ดของคุณอ่านยากขึ้น นอกจากนี้ถ้าฉันจำได้อย่างถูกต้องTryGetValueก็ไม่ได้เป็นอะตอมมิก / เธรดที่ปลอดภัยดังนั้นคุณสามารถทำการตรวจสอบการมีอยู่ของตัวเองได้อย่างง่ายดายและอีกวิธีหนึ่งในการคว้าและใช้งานค่า
Marie

12

นี่ไม่ใช่รหัสกลิ่นหรือรูปแบบการต่อต้านเช่นเดียวกับการใช้ฟังก์ชั่นสไตล์ TryGet ที่มีพารามิเตอร์ out คือ idiomatic C # อย่างไรก็ตามมี 3 ตัวเลือกใน C # เพื่อทำงานกับพจนานุกรมดังนั้นหากคุณแน่ใจว่าคุณใช้ตัวเลือกที่ถูกต้องสำหรับสถานการณ์ของคุณ ฉันคิดว่าฉันรู้ว่าข่าวลือที่ว่ามีปัญหาในการใช้พารามิเตอร์ out มาจากไหนดังนั้นฉันจะจัดการกับมันในตอนท้าย

คุณลักษณะใดที่จะใช้เมื่อทำงานกับพจนานุกรม C #:

  1. หากคุณแน่ใจว่ารหัสจะอยู่ในพจนานุกรมให้ใช้คุณสมบัติรายการ [TKey]
  2. หากคีย์โดยทั่วไปควรอยู่ในพจนานุกรม แต่ไม่ดี / หายาก / มีปัญหาที่ไม่มีคุณควรใช้ลอง ... จับเพื่อให้เกิดข้อผิดพลาดและจากนั้นคุณสามารถพยายามจัดการข้อผิดพลาดได้อย่างสง่างาม
  3. หากคุณไม่แน่ใจว่าคีย์จะอยู่ในพจนานุกรมหรือไม่ให้ใช้ TryGet พร้อมกับพารามิเตอร์ out

หากต้องการให้เหตุผลสิ่งนี้จำเป็นต้องอ้างถึงเอกสารประกอบสำหรับพจนานุกรม TryGetValue ภายใต้ "ข้อสังเกต" :

วิธีนี้รวมฟังก์ชันการทำงานของวิธีการที่มีKeyKeyและคุณสมบัติของรายการ [TKey]

...

ใช้วิธี TryGetValue หากรหัสของคุณพยายามเข้าถึงคีย์ที่ไม่ได้อยู่ในพจนานุกรมบ่อยครั้ง การใช้วิธีนี้มีประสิทธิภาพมากกว่าการจับ KeyNotFoundException ที่ส่งออกโดยคุณสมบัติ Item [TKey]

วิธีการนี้เข้าใกล้การดำเนินการ O (1)

เหตุผลทั้งหมดที่ TryGetValue มีอยู่คือทำหน้าที่เป็นวิธีที่สะดวกกว่าในการใช้ประกอบด้วยKeyและ Item [TKey] ในขณะที่หลีกเลี่ยงการค้นหาพจนานุกรมสองครั้ง - ดังนั้นการแกล้งทำเป็นไม่มีอยู่และทำสองสิ่งที่ทำด้วยตนเอง ทางเลือก.

ในทางปฏิบัติผมไม่ค่อยได้เคยใช้พจนานุกรมดิบเนื่องจากคำพังเพยนี้ง่าย: เลือกทั่วไปมากที่สุดระดับ / ภาชนะบรรจุที่ช่วยให้คุณทำงานที่คุณต้องการ พจนานุกรมไม่ได้ออกแบบมาเพื่อค้นหาตามค่าแทนที่จะเป็นคีย์ (ตัวอย่าง) ดังนั้นหากเป็นสิ่งที่คุณต้องการมันอาจจะเหมาะสมกว่าที่จะใช้โครงสร้างทางเลือก ฉันคิดว่าฉันอาจใช้พจนานุกรมหนึ่งครั้งในโครงการพัฒนาปีที่แล้วที่ฉันทำเพราะมันไม่ค่อยเป็นเครื่องมือที่เหมาะสมสำหรับงานที่ฉันพยายามทำ พจนานุกรมไม่ใช่มีดของ Swiss Army ของกล่องเครื่องมือ C #

มีอะไรผิดปกติกับพารามิเตอร์ออก?

CA1021: หลีกเลี่ยงพารามิเตอร์

แม้ว่าค่าที่ส่งคืนจะเป็นเรื่องธรรมดาและมีการใช้งานอย่างหนัก แต่แอปพลิเคชันที่ถูกต้องของพารามิเตอร์ out และ ref ต้องการการออกแบบระดับกลางและทักษะการเขียนโปรแกรม สถาปนิกห้องสมุดที่ออกแบบมาสำหรับผู้ชมทั่วไปไม่ควรคาดหวังให้ผู้ใช้ทำงานกับพารามิเตอร์หลักหรือพารามิเตอร์อ้างอิง

ฉันเดาว่าเป็นสิ่งที่คุณได้ยินว่าพารามิเตอร์เป็นสิ่งที่ต่อต้านการทำแพทเทิร์น เช่นเดียวกับกฎทั้งหมดคุณควรอ่านอย่างใกล้ชิดเพื่อทำความเข้าใจกับ 'ทำไม' และในกรณีนี้มีการกล่าวถึงอย่างชัดเจนว่ารูปแบบลองไม่ละเมิดกฎนี้อย่างไร :

วิธีการที่ใช้รูปแบบลองเช่น System.Int32.TryParse ไม่เพิ่มการละเมิดนี้


รายการคำแนะนำทั้งหมดนั้นเป็นสิ่งที่ฉันต้องการให้คนอ่านเพิ่มขึ้น สำหรับผู้ที่ติดตามนี่คือรากของมัน docs.microsoft.com/en-us/visualstudio/code-quality/ …
Peter Wone

10

มีอย่างน้อยสองวิธีที่หายไปจากพจนานุกรม C # ที่ในความคิดของฉันล้างรหัสมากในสถานการณ์จำนวนมากในภาษาอื่น ๆ วิธีแรกคือคืนค่าOptionซึ่งช่วยให้คุณสามารถเขียนโค้ดได้ดังนี้ใน Scala:

dict.get("key").map(doSomethingWith)

ประการที่สองคืนค่าเริ่มต้นที่ผู้ใช้ระบุหากไม่พบคีย์:

doSomethingWith(dict.getOrElse("key", "key not found"))

มีบางอย่างที่จะกล่าวว่าสำหรับการใช้สำนวนที่ภาษาให้เมื่อเหมาะสมเช่นTryรูปแบบ แต่นั่นไม่ได้หมายความว่าคุณจะต้องใช้เฉพาะสิ่งที่ภาษาให้ เราเป็นโปรแกรมเมอร์ มันก็โอเคที่จะสร้าง abstractions ใหม่เพื่อทำให้สถานการณ์เฉพาะของเราง่ายต่อการเข้าใจโดยเฉพาะถ้ามันกำจัดการซ้ำซ้อนมากมาย หากคุณต้องการบางสิ่งบางอย่างเช่นการค้นหาแบบย้อนกลับหรือวนซ้ำผ่านค่าต่างๆ สร้างส่วนต่อประสานที่คุณต้องการ


ฉันชอบตัวเลือกที่ 2 บางทีฉันอาจจะต้องมีการเขียนวิธีการขยายหรือสอง :)
อดัม B

2
ในวิธีการขยาย c # ด้านบวกหมายความว่าคุณสามารถใช้สิ่งเหล่านี้ด้วยตัวเอง
jk

5

นั่นคือโค้ด 4 บรรทัดที่จะทำสิ่งต่อไปนี้: DoSomethingWith(dict["key"])

ฉันยอมรับว่านี่ไม่เหมาะสม กลไกที่ฉันชอบใช้ในกรณีนี้โดยที่ value คือประเภท struct คือ:

public static V? TryGetValue<K, V>(
      this Dictionary<K, V> dict, K key) where V : struct => 
  dict.TryGetValue(key, out V v)) ? new V?(v) : new V?();

ตอนนี้เรามีเวอร์ชันใหม่ของการTryGetValueส่งคืนint?นั้น จากนั้นเราสามารถทำเคล็ดลับที่คล้ายกันสำหรับการขยายT?:

public static void DoIt<T>(
      this T? item, Action<T> action) where T : struct
{
  if (item != null) action(item.GetValueOrDefault());
}

และตอนนี้รวมกัน:

dict.TryGetValue("key").DoIt(DoSomethingWith);

และเรากำลังพูดถึงคำสั่งที่ชัดเจนและชัดเจน

ฉันได้ยินมาว่าการใช้คีย์เวิร์ด out เป็นรูปแบบการต่อต้านเพราะทำให้ฟังก์ชั่นกลายพันธุ์พารามิเตอร์ของพวกเขา

ฉันจะพูดน้อยลงอย่างรุนแรงและพูดว่าการหลีกเลี่ยงการกลายพันธุ์เมื่อเป็นไปได้เป็นความคิดที่ดี

ฉันพบว่าตัวเองต้องการพจนานุกรม "กลับด้าน" บ่อยครั้งที่ฉันพลิกคีย์และค่าต่างๆ

จากนั้นนำไปใช้หรือรับพจนานุกรมสองทิศทาง พวกเขาเขียนตรงไปตรงมาหรือมีการใช้งานมากมายบนอินเทอร์เน็ต มีตัวอย่างของการใช้งานที่นี่เช่น:

/programming/268321/bidirectional-1-to-1-dictionary-in-c-sharp

ในทำนองเดียวกันฉันมักจะทำซ้ำผ่านรายการในพจนานุกรมและพบว่าตนเองกำลังแปลงคีย์หรือค่าไปยังรายการ ฯลฯ เพื่อทำสิ่งนี้ให้ดีขึ้น

แน่นอนว่าเราทุกคนทำ

ฉันรู้สึกว่ามีวิธีการใช้พจนานุกรมที่ดีกว่าและสง่างามกว่าอยู่เกือบตลอดเวลา แต่ฉันรู้สึกแย่

ถามตัวเองว่า "ถ้าฉันมีชั้นเรียนอื่นนอกเหนือจากDictionaryที่ใช้การดำเนินการที่แน่นอนที่ฉันต้องการจะทำ; ชั้นนั้นจะมีลักษณะอย่างไร?" จากนั้นเมื่อคุณตอบคำถามแล้วให้ใช้คลาสนั้น คุณเป็นโปรแกรมเมอร์คอมพิวเตอร์ แก้ไขปัญหาของคุณด้วยการเขียนโปรแกรมคอมพิวเตอร์!


ขอบคุณ คุณคิดว่าคุณสามารถอธิบายเพิ่มเติมได้อีกเล็กน้อยว่าวิธีการกระทำนั้นกำลังทำอะไรอยู่? ฉันใหม่กับการกระทำ
อดัม B

1
@AdamB การกระทำเป็นวัตถุที่แสดงถึงความสามารถในการเรียกวิธีการเป็นโมฆะ อย่างที่คุณเห็นในด้านผู้โทรเราจะผ่านชื่อของวิธีการโมฆะซึ่งรายการพารามิเตอร์ตรงกับอาร์กิวเมนต์ประเภททั่วไปของการกระทำ ในด้านผู้โทรการดำเนินการที่ถูกเรียกเช่นวิธีอื่น ๆ
Eric Lippert

1
@ AdamB: คุณคิดว่าAction<T>คล้ายกันมากinterface IAction<T> { void Invoke(T t); }แต่ด้วยกฎที่ผ่อนปรนมากเกี่ยวกับสิ่งที่ "ใช้งาน" อินเทอร์เฟซนั้นและวิธีการInvokeเรียกใช้ ถ้าคุณต้องการทราบเพิ่มเติมเรียนรู้เกี่ยวกับ "ผู้รับมอบสิทธิ์" ใน C # แล้วเรียนรู้เกี่ยวกับการแสดงออกแลมบ์ดา
Eric Lippert

โอเคฉันคิดว่าฉันเข้าใจแล้ว ดังนั้นการกระทำให้คุณใส่วิธีเป็นพารามิเตอร์สำหรับ DoIt () และเรียกวิธีการนั้นว่า
อดัม B

@ AdamB: ถูกต้อง นี่คือตัวอย่างของ "การโปรแกรมเชิงฟังก์ชันที่มีฟังก์ชันลำดับสูงกว่า" ฟังก์ชั่นส่วนใหญ่ใช้ข้อมูลเป็นอาร์กิวเมนต์ ฟังก์ชันลำดับที่สูงกว่าใช้ฟังก์ชันเป็นอาร์กิวเมนต์แล้วทำสิ่งต่างๆกับฟังก์ชันเหล่านั้น ในขณะที่คุณเรียนรู้ C # มากขึ้นคุณจะเห็นว่า LINQ มีการใช้งานอย่างสมบูรณ์กับฟังก์ชันการสั่งซื้อที่สูงขึ้น
Eric Lippert

4

TryGetValue()สร้างเป็นสิ่งที่จำเป็นเท่านั้นถ้าคุณไม่ทราบว่า "ที่สำคัญ" เป็นปัจจุบันเป็นคีย์ในพจนานุกรมหรือไม่มิฉะนั้นDoSomethingWith(dict["key"])เป็นที่ถูกต้องสมบูรณ์

อาจใช้วิธี "สกปรกน้อยกว่า" เพื่อใช้ContainsKey()เป็นเช็คแทน


6
วิธีการ "A" ที่สกปรกน้อยกว่า "อาจจะใช้ containKey () เป็นเช็คแทน" ฉันไม่เห็นด้วย. ในฐานะที่ก่อให้เกิดผลลัพธ์เป็นTryGetValueเป็นอย่างน้อยก็จะทำให้มันยากที่จะลืมที่จะจัดการกับกรณีที่ว่างเปล่า เป็นการดีที่ฉันหวังว่าสิ่งนี้จะส่งคืนทางเลือก
Alexander - Reinstate Monica

1
มีปัญหาที่อาจเกิดขึ้นกับContainsKeyแนวทางสำหรับแอพพลิเคชั่นแบบมัลติเธรดซึ่งเป็นช่วงเวลาของการตรวจสอบเวลาที่ใช้ (TOCTOU) ช่องโหว่ เกิดอะไรขึ้นถ้าเธรดอื่นลบคีย์ระหว่างการเรียกไปที่ContainsKeyและGetValue?
David Hammen

2
@JAD Optionals เขียน คุณสามารถมีOptional<Optional<T>>
Alexander - Reinstate Monica

2
@DavidHammen ต้องมีความชัดเจนคุณจะต้องอยู่บนTryGetValue ConcurrentDictionaryพจนานุกรมทั่วไปจะไม่ถูกซิงโครไนซ์
Alexander - Reinstate Monica

2
@JAD ไม่ (จาวาประเภทที่เป็นตัวเลือกไม่ต้องเริ่มเลย) ฉันกำลังพูดถึงนามธรรมมากขึ้น
Alexander - Reinstate Monica

2

คำตอบอื่น ๆ มีจุดยอดเยี่ยมดังนั้นฉันจะไม่พูดซ้ำอีกที่นี่ แต่ฉันจะมุ่งเน้นที่ส่วนนี้ซึ่งดูเหมือนว่าจะไม่สนใจส่วนใหญ่แล้ว:

ในทำนองเดียวกันฉันมักจะทำซ้ำผ่านรายการในพจนานุกรมและพบว่าตนเองกำลังแปลงคีย์หรือค่าไปยังรายการ ฯลฯ เพื่อทำสิ่งนี้ให้ดีขึ้น

จริงๆแล้วมันค่อนข้างง่ายที่จะวนซ้ำในพจนานุกรมเนื่องจากมันใช้IEnumerable:

var dict = new Dictionary<int, string>();

foreach ( var item in dict ) {
    Console.WriteLine("{0} => {1}", item.Key, item.Value);
}

ถ้าคุณชอบ Linq มันก็ใช้ได้ดี:

dict.Select(x=>x.Value).WhateverElseYouWant();

โดยสรุปแล้วฉันไม่พบว่าพจนานุกรมเป็นปฏิปักษ์ต่อกัน - เป็นเพียงเครื่องมือเฉพาะที่มีการใช้งานเฉพาะ

นอกจากนี้สำหรับข้อมูลที่เฉพาะเจาะจงยิ่งขึ้นให้ตรวจสอบSortedDictionary(ใช้ต้นไม้ RB สำหรับประสิทธิภาพที่คาดการณ์ได้มากขึ้น) และSortedList(ซึ่งเป็นพจนานุกรมที่มีชื่อที่สับสนที่เสียสละความเร็วในการแทรกสำหรับความเร็วในการค้นหา แต่จะส่องหากคุณกำลังมอง จัดเรียงชุด) ฉันมีกรณีที่การแทนที่Dictionaryด้วยSortedDictionaryผลลัพธ์ในลำดับความสำคัญของการดำเนินการเร็วขึ้น (แต่มันอาจเกิดขึ้นในทางกลับกันด้วย)


1

หากคุณรู้สึกว่าการใช้พจนานุกรมอึดอัดใจอาจไม่ใช่ทางเลือกที่เหมาะสมสำหรับปัญหาของคุณ พจนานุกรมดีมาก แต่เหมือนผู้วิจารณ์คนหนึ่งสังเกตเห็นบ่อยครั้งที่พวกมันถูกใช้เป็นทางลัดสำหรับบางสิ่งที่ควรเป็นคลาส หรือตัวพจนานุกรมเองอาจถูกต้องเป็นวิธีการเก็บข้อมูลหลัก แต่ควรมีคลาสตัวล้อมรอบเพื่อให้วิธีการบริการที่ต้องการ

มีการพูดถึง Dict [key] มากกับ TryGet สิ่งที่ฉันใช้บ่อยมากคือการวนซ้ำพจนานุกรมโดยใช้ KeyValuePair เห็นได้ชัดว่านี่เป็นโครงสร้างที่รู้จักกันน้อย

ประโยชน์หลักของพจนานุกรมคือมันเร็วจริง ๆ เมื่อเปรียบเทียบกับคอลเลกชันอื่น ๆ เมื่อจำนวนของรายการเพิ่มขึ้น หากคุณต้องตรวจสอบว่ามีกุญแจอยู่มากคุณอาจต้องถามตัวเองว่าการใช้พจนานุกรมของคุณเหมาะสมหรือไม่ ในฐานะลูกค้าคุณมักจะรู้ว่าคุณใส่อะไรลงไปในนั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.