สลับหรือพจนานุกรมเมื่อกำหนดให้กับวัตถุใหม่


12

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

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

จากการสร้างนั้นฉันได้สูญเสียรอบ CPU และหน่วยความจำในการสร้างวัตถุ 3 รายการทำสิ่งที่ตัวสร้างของพวกเขาอาจมีและสิ้นสุดลงด้วยการใช้หนึ่งในนั้น ฉันยังเชื่อว่าการทำแผนที่วัตถุอื่น ๆfooDict[0]ในกรณีนี้จะทำให้พวกเขาอ้างอิงในสิ่งเดียวกันแทนที่จะสร้างอินสแตนซ์ใหม่Fooตามที่ตั้งใจไว้ วิธีแก้ปัญหาคือใช้แลมบ์ดาแทน:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

นี่เป็นจุดที่ทำให้สับสนหรือไม่? มันง่ายที่จะพลาด()ในตอนท้าย หรือการทำแผนที่กับฟังก์ชั่น / การแสดงออกเป็นเรื่องธรรมดามาก? ทางเลือกอื่นคือใช้สวิตช์:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

การอุทธรณ์ใดที่ยอมรับได้มากกว่า

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

2
คุณจะได้รับอินสแตนซ์เดียวกันคืนมาทุกครั้งที่คุณค้นหาโดยใช้คีย์เดียวกัน (เหมือนในfooDict[0] is fooDict[0]) ด้วยแลมบ์ดาและสวิตช์นี่ไม่ใช่กรณี
ratchet freak

@ ratchetfreak ใช่ฉันรู้จริงเมื่อฉันพิมพ์ตัวอย่าง ฉันคิดว่าฉันจดบันทึกมันที่ไหนซักแห่ง
KChaloux

1
ฉันเดาความจริงที่ว่าคุณใส่มันไว้ในค่าคงที่อย่างชัดเจนว่าคุณต้องการวัตถุที่สร้างขึ้นเพื่อให้ไม่แน่นอน แต่ถ้าวันหนึ่งคุณสามารถทำให้พวกมันไม่เปลี่ยนรูปแล้วส่งคืนวัตถุโดยตรงจะเป็นทางออกที่ดีที่สุด คุณสามารถใส่ dict ลงในฟิลด์ const และต้องเสียค่าใช้จ่ายในการสร้างเพียงครั้งเดียวในแอปพลิเคชันทั้งหมด
Laurent Bourgault-Roy

คำตอบ:


7

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

ฉันไม่สนใจข้อกังวลของคุณเกี่ยวกับรอบการทำงานของ CPU ดังที่คุณได้กล่าวไว้ในความคิดเห็นว่าวิธีการที่ไม่ใช่แลมบ์ดาไม่ได้ให้สิ่งที่คุณต้องการ

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

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

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


น่าเสียดายที่ประมาณหนึ่งเดือนที่ผ่านมาทีมของฉันประกอบด้วยฉันคนเดียว ฉันไม่คิดว่าเกี่ยวข้องกับรูปแบบของโรงงาน นั่นเป็นข้อสังเกตที่เรียบร้อยจริงๆ
KChaloux

1
@KChaloux: แน่นอนว่าคุณกำลังใช้เพียงโรงงานรูปแบบวิธีของคุณcase 0: quux = new Foo(); break;จะกลายเป็นcase 0: return new Foo();ซึ่งเป็นตรงไปตรงมาเป็นเรื่องง่ายที่จะเขียนและมากพอที่จะอ่านง่ายกว่า{ 0, () => new Foo() }
สาธารณรัฐประชาธิปไตยประชาชนลาว

@pdr นั่นปรากฏขึ้นในรหัสไม่กี่ที่แล้ว อาจมีเหตุผลที่ดีในการสร้างวิธีการจากโรงงานบนวัตถุที่เป็นแรงบันดาลใจให้กับคำถามนี้ แต่ฉันคิดว่ามันน่าสนใจพอที่จะถามด้วยตัวเอง
KChaloux

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

@pdr มัวเมาเป็นแข็งแกร่งคำที่จะใช้ที่นี่ ข้อควรพิจารณาเพิ่มเติมเมื่อตัดสินใจวิธีจัดการกับการแม็พแบบครั้งเดียวระหว่างค่า ฉันยอมรับว่าในกรณีที่มีการทำซ้ำวิธีที่ดีที่สุดในการแยกวิธีการสร้าง
KChaloux

7

C # 4.0 ให้Lazy<T>คลาสซึ่งคล้ายกับโซลูชันที่สองของคุณ แต่ส่งเสียง "Lazy initialization" อย่างชัดเจนยิ่งขึ้น

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}

Ooh ฉันไม่รู้
KChaloux

โอ้เยี่ยมเลย!
Laurent Bourgault-Roy

2
อย่างไรก็ตามเมื่อ Lazy.Value ถูกเรียกใช้มันจะใช้อินสแตนซ์เดียวกันกับอายุการใช้งานของมัน ดูการเริ่มต้น Lazy
Dan Lyons

แน่นอนมิฉะนั้นมันจะไม่เริ่มต้นขี้เกียจเพียงเริ่มต้นใหม่ทุกครั้ง
Avner Shahar-Kashtan

OP ระบุว่าเขาต้องการให้สร้างอินสแตนซ์ใหม่ในแต่ละครั้ง โซลูชันที่สองที่มี lambdas และโซลูชันที่สามที่มีสวิตช์ทำเช่นนั้นในขณะที่โซลูชันแรกและการใช้งาน Lazy <T> ไม่ทำ
Dan Lyons

2

โวหารฉันคิดว่าการอ่านมีค่าเท่ากันระหว่างพวกเขา Dictionaryมันเป็นเรื่องง่ายที่จะทำฉีดพึ่งพากับ

อย่าลืมว่าคุณต้องตรวจสอบว่ามีคีย์อยู่Dictionaryหรือไม่และต้องระบุทางเลือกหากไม่มี

ฉันต้องการswitchคำสั่งสำหรับเส้นทางรหัสคงที่และDictionaryสำหรับเส้นทางรหัสแบบไดนามิก (ที่คุณอาจเพิ่มหรือลบรายการ) คอมไพเลอร์อาจจะสามารถที่จะดำเนินการเพิ่มประสิทธิภาพคงที่บางอย่างกับว่ามันไม่สามารถมีswitchDictionary

ที่น่าสนใจDictionaryรูปแบบนี้เป็นสิ่งที่ผู้คนทำใน Python บางครั้งเนื่องจาก Python ไม่มีswitchคำสั่ง มิฉะนั้นพวกเขาใช้โซ่ if-else


1

โดยทั่วไปแล้วฉันไม่ต้องการ

Func<int, IBigObject>สิ่งที่อยู่นานนี้ควรทำงานร่วมกับ จากนั้นแหล่งที่มาของการทำแผนที่ของคุณสามารถเป็นพจนานุกรมหรือวิธีการที่มีคำสั่งเปลี่ยนหรือโทรบริการบนเว็บหรือการค้นหาไฟล์ ... อะไรก็ตาม

สำหรับการนำไปใช้ฉันต้องการพจนานุกรมเนื่องจากง่ายกว่า refactored จาก 'พจนานุกรมรหัสยาก, คีย์การค้นหา, ส่งคืนผลลัพธ์' เป็น 'โหลดพจนานุกรมจากไฟล์, คีย์การค้นหา, ส่งคืนผลลัพธ์'

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