ปลอดภัยสำหรับโครงสร้างในการใช้อินเทอร์เฟซหรือไม่?


94

ฉันดูเหมือนจะจำได้ว่าอ่านบางอย่างเกี่ยวกับการที่โครงสร้างในการใช้อินเทอร์เฟซใน CLR ผ่าน C # ไม่ดี แต่ดูเหมือนว่าฉันจะไม่พบอะไรเลย มันแย่ไหม? มีผลที่ไม่พึงประสงค์จากการทำเช่นนั้นหรือไม่?

public interface Foo { Bar GetBar(); }
public struct Fubar : Foo { public Bar GetBar() { return new Bar(); } }

คำตอบ:


45

มีหลายสิ่งที่เกิดขึ้นในคำถามนี้ ...

เป็นไปได้ที่โครงสร้างจะใช้อินเทอร์เฟซ แต่มีข้อกังวลเกี่ยวกับการหล่อการเปลี่ยนแปลงและประสิทธิภาพ ดูโพสต์นี้สำหรับรายละเอียดเพิ่มเติม: https://docs.microsoft.com/en-us/archive/blogs/abhinaba/c-structs-and-interface

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


3
"ผลของการชกมวยการดำเนินงานที่เปลี่ยนแปลงสถานะภายในของโครงสร้างอาจทำงานไม่ถูกต้อง" ยกตัวอย่างและรับคำตอบ

2
@ Will: ไม่แน่ใจว่าคุณกำลังอ้างถึงอะไรในความคิดเห็นของคุณ บล็อกโพสต์ที่ฉันอ้างถึงมีตัวอย่างที่แสดงให้เห็นว่าการเรียกเมธอดอินเทอร์เฟซบนโครงสร้างไม่ได้เปลี่ยนค่าภายใน
Scott Dorman

12
@ScottDorman: ในบางกรณีการมีโครงสร้างใช้อินเทอร์เฟซอาจช่วยหลีกเลี่ยงการชกมวยได้ ตัวอย่างที่สำคัญคือIComparable<T>และIEquatable<T>. การจัดเก็บโครงสร้างFooในตัวแปรประเภทIComparable<Foo>จะต้องใช้การชกมวย แต่ถ้าประเภททั่วไปTถูก จำกัด ไว้ที่ประเภทIComparable<T>หนึ่งอาจเปรียบเทียบกับอีกประเภทหนึ่งTโดยไม่ต้องใส่กล่องอย่างใดอย่างหนึ่งและไม่ต้องรู้อะไรเกี่ยวกับสิ่งTอื่นนอกเหนือจากนั้นก็ใช้ข้อ จำกัด พฤติกรรมที่เป็นประโยชน์ดังกล่าวเกิดขึ้นได้โดยความสามารถของโครงสร้างในการใช้อินเทอร์เฟซเท่านั้น ที่มีการพูดกันว่า ...
supercat

3
... มันอาจจะดีถ้ามีวิธีการประกาศว่าควรพิจารณาอินเทอร์เฟซเฉพาะกับโครงสร้างที่ไม่มีกล่องเท่านั้นเนื่องจากมีบริบทบางอย่างที่ไม่สามารถทำได้สำหรับวัตถุคลาสหรือโครงสร้างแบบกล่องที่จะมีสิ่งที่ต้องการ พฤติกรรม.
supercat

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

185

เนื่องจากไม่มีใครให้คำตอบนี้อย่างชัดเจนฉันจะเพิ่มสิ่งต่อไปนี้:

การใช้งานอินเทอร์เฟซบนโครงสร้างไม่มีผลเสียใด ๆ

ตัวแปรใด ๆของประเภทอินเทอร์เฟซที่ใช้ในการเก็บโครงสร้างจะส่งผลให้มีการใช้ค่าแบบบรรจุกล่องของโครงสร้างนั้น หากโครงสร้างไม่เปลี่ยนรูป (เป็นสิ่งที่ดี) นี่เป็นปัญหาด้านประสิทธิภาพที่แย่ที่สุดเว้นแต่คุณจะ:

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

ทั้งสองอย่างนี้ไม่น่าจะเป็นไปได้ แต่คุณน่าจะทำอย่างใดอย่างหนึ่งต่อไปนี้:

Generics

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

class Foo<T> : IEquatable<Foo<T>> where T : IEquatable<T>
{
    private readonly T a;

    public bool Equals(Foo<T> other)
    {
         return this.a.Equals(other.a);
    }
}
  1. เปิดใช้งานการใช้โครงสร้างเป็นพารามิเตอร์ชนิด
    • ตราบเท่าที่ไม่มีการใช้ข้อ จำกัด อื่น ๆ เช่นnew()หรือclassใช้
  2. อนุญาตให้หลีกเลี่ยงการชกมวยในโครงสร้างที่ใช้ในลักษณะนี้

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

หาก thisType เป็นชนิดค่าและวิธีการนี้ใช้วิธีนี้ ptr จะถูกส่งผ่านโดยไม่มีการปรับเปลี่ยนเป็นตัวชี้ 'this' ไปยังคำสั่งวิธีการเรียกสำหรับการใช้เมธอดโดย thisType

สิ่งนี้จะหลีกเลี่ยงการชกมวยและเนื่องจากประเภทค่าที่ใช้งานอินเทอร์เฟซจะต้องใช้วิธีการนี้จึงจะไม่มีการชกมวยเกิดขึ้น ในตัวอย่างด้านบนการEquals()เรียกใช้จะทำโดยไม่มีช่องนี้ a 1 .

API แรงเสียดทานต่ำ

structs ส่วนใหญ่ควรจะมีดั้งเดิมเหมือนความหมายที่บิตค่าเหมือนกันจะถือว่าเท่ากับ2 รันไทม์จะให้พฤติกรรมดังกล่าวโดยนัยEquals()แต่อาจช้า นอกจากนี้ความเท่าเทียมกันโดยปริยายนี้จะไม่ถูกเปิดเผยว่าเป็นการใช้งานIEquatable<T>และป้องกันไม่ให้มีการใช้โครงสร้างเป็นกุญแจสำหรับพจนานุกรมได้อย่างง่ายดายเว้นแต่จะนำไปใช้อย่างชัดเจนด้วยตนเอง ดังนั้นจึงเป็นเรื่องปกติที่ประเภทโครงสร้างสาธารณะหลายประเภทจะประกาศว่าพวกเขานำไปใช้IEquatable<T>(โดยที่Tพวกเขาเอง) เพื่อทำให้สิ่งนี้ง่ายขึ้นและทำงานได้ดีขึ้นรวมทั้งสอดคล้องกับพฤติกรรมของประเภทค่าที่มีอยู่จำนวนมากภายใน CLR BCL

พื้นฐานทั้งหมดใน BCL ใช้ขั้นต่ำ:

  • IComparable
  • IConvertible
  • IComparable<T>
  • IEquatable<T>(และดังนั้นIEquatable)

หลายคนยังใช้IFormattableอีกหลายประเภทของค่าที่ระบบกำหนดเช่น DateTime, TimeSpan และ Guid ก็ใช้จำนวนมากหรือทั้งหมดเช่นกัน หากคุณใช้งานประเภทที่ 'มีประโยชน์อย่างกว้างขวาง' ในทำนองเดียวกันเช่นโครงสร้างจำนวนเชิงซ้อนหรือค่าความกว้างที่คงที่แบบข้อความการใช้อินเทอร์เฟซทั่วไปเหล่านี้ (อย่างถูกต้อง) จะทำให้โครงสร้างของคุณมีประโยชน์และใช้งานได้มากขึ้น

การยกเว้น

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

อินเทอร์เฟซจำนวนมากไม่ได้หมายความถึงความไม่แน่นอน (เช่นIFormattable) และเป็นวิธีการใช้สำนวนเพื่อแสดงฟังก์ชันการทำงานบางอย่างในรูปแบบที่สอดคล้องกัน บ่อยครั้งที่ผู้ใช้โครงสร้างจะไม่สนใจเกี่ยวกับค่าใช้จ่ายในการชกมวยสำหรับพฤติกรรมดังกล่าว

สรุป

เมื่อทำอย่างสมเหตุสมผลในประเภทค่าที่ไม่เปลี่ยนรูปแบบการใช้งานอินเทอร์เฟซที่มีประโยชน์เป็นความคิดที่ดี


หมายเหตุ:

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

List<int> l = new List<int>();
foreach(var x in l)
    ;//no-op

ตัวแจงนับที่ส่งคืนโดยรายการเป็นโครงสร้างการเพิ่มประสิทธิภาพเพื่อหลีกเลี่ยงการจัดสรรเมื่อระบุรายการ (พร้อมผลลัพธ์ที่น่าสนใจบางอย่าง) อย่างไรก็ตามความหมายของ foreach ระบุว่าหาก enumerator ดำเนินการIDisposableแล้วDispose()จะถูกเรียกเมื่อการทำซ้ำเสร็จสิ้น เห็นได้ชัดว่าการที่สิ่งนี้เกิดขึ้นผ่านการโทรแบบบรรจุกล่องจะกำจัดผลประโยชน์ใด ๆ ของตัวแจงนับที่เป็นโครงสร้าง (อันที่จริงมันจะแย่กว่านี้) ที่แย่กว่านั้นคือถ้าการโทรออกได้ปรับเปลี่ยนสถานะของตัวแจงนับไม่ทางใดก็ทางหนึ่งสิ่งนี้จะเกิดขึ้นในอินสแตนซ์แบบบรรจุกล่องและอาจมีการใช้ข้อบกพร่องที่ละเอียดอ่อนมากมายในกรณีที่ซับซ้อน ดังนั้น IL ที่ปล่อยออกมาในสถานการณ์เช่นนี้คือ:

IL_0001: newobj System.Collections.Generic.List..ctor
IL_0006: stloc.0     
IL_0007: nop         
IL_0008: ldloc.0     
IL_0009: callvirt System.Collections.Generic.List.GetEnumerator
IL_000E: stloc 2     
IL_000F: br.s IL_0019
IL_0011: ldloca.s 02 
IL_0013: เรียก System.Collections.Generic.List.get_Current
IL_0018: stloc 1     
IL_0019: ldloca.s 02 
IL_001B: เรียก System.Collections.Generic.List.MoveNext
IL_0020: stloc 3     
IL_0021: ldloc 3     
IL_0022: brtrue.s IL_0011
IL_0024: ลา. s IL_0035
IL_0026: ldloca.s 02 
IL_0028: ถูก จำกัด System.Collections.Generic.List.Enumerator
IL_002E: callvirt System.IDisposable.Dispose
IL_0033: nop         
IL_0034: สิ้นสุดในที่สุด  

ดังนั้นการนำ IDisposable ไปใช้ไม่ก่อให้เกิดปัญหาด้านประสิทธิภาพใด ๆ และแง่มุมที่ไม่แน่นอน (น่าเสียใจ) ของตัวแจงนับจะถูกเก็บรักษาไว้หากเมธอด Dispose ทำอะไรได้จริง!

2: double และ float เป็นข้อยกเว้นของกฎนี้โดยที่ค่า NaN ไม่ถือว่าเท่ากัน


1
เว็บไซต์ egheadcafe.com ได้ย้ายไปแล้ว แต่ไม่ได้ผลดีกับการรักษาเนื้อหาไว้ ฉันพยายามแล้ว แต่ไม่พบเอกสารต้นฉบับของeggheadcafe.com/software/aspnet/31702392/…ขาดความรู้เกี่ยวกับ OP (PS +1 สำหรับบทสรุปที่ยอดเยี่ยม)
Abel

2
นี่เป็นคำตอบที่ดี แต่ฉันคิดว่าคุณสามารถปรับปรุงได้โดยการย้าย "สรุป" ไปด้านบนเป็น "TL; DR" การให้ข้อสรุปก่อนจะช่วยให้ผู้อ่านทราบว่าคุณกำลังจะไปที่ใด
Hans

ควรจะมีการเตือนเมื่อคอมไพเลอร์หล่อไปยังstruct interface
Jalal

8

ในบางกรณีอาจเป็นการดีสำหรับโครงสร้างในการติดตั้งอินเทอร์เฟซ (หากไม่เป็นประโยชน์ก็เป็นที่น่าสงสัยว่าผู้สร้าง. net จะจัดหาให้) หากโครงสร้างใช้อินเทอร์เฟซแบบอ่านอย่างเดียวเช่นการIEquatable<T>จัดเก็บโครงสร้างในตำแหน่งหน่วยเก็บข้อมูล (ตัวแปรพารามิเตอร์องค์ประกอบอาร์เรย์ ฯลฯ ) ประเภทIEquatable<T>จะกำหนดให้เป็นประเภทกล่อง (โครงสร้างแต่ละประเภทกำหนดสิ่งต่างๆไว้สองประเภท: ที่เก็บข้อมูล ประเภทสถานที่ซึ่งทำหน้าที่เป็นประเภทค่าและประเภทฮีป - อ็อบเจ็กต์ซึ่งทำงานเป็นประเภทคลาสประเภทแรกสามารถแปลงเป็นประเภทที่สองได้โดยปริยาย - "มวย" - และประเภทที่สองอาจถูกแปลงเป็นประเภทแรกผ่านการร่ายอย่างชัดเจน - "แกะกล่อง") เป็นไปได้ที่จะใช้ประโยชน์จากการใช้งานโครงสร้างของอินเทอร์เฟซโดยไม่มีการชกมวยอย่างไรก็ตามโดยใช้สิ่งที่เรียกว่า generics ที่มีข้อ จำกัด

ตัวอย่างเช่นถ้าใครมีวิธีการCompareTwoThings<T>(T thing1, T thing2) where T:IComparable<T>วิธีดังกล่าวสามารถโทรthing1.Compare(thing2)ได้โดยไม่ต้องกล่องหรือthing1 thing2หากthing1จะเกิดขึ้นเช่นการที่เวลาทำงานจะรู้ว่าเมื่อมันสร้างรหัสสำหรับInt32 CompareTwoThings<Int32>(Int32 thing1, Int32 thing2)เนื่องจากจะทราบประเภทที่แน่นอนของทั้งสิ่งที่โฮสต์วิธีการและสิ่งที่ส่งผ่านเป็นพารามิเตอร์จึงไม่จำเป็นต้องใส่กล่องอย่างใดอย่างหนึ่ง

ปัญหาที่ใหญ่ที่สุดเกี่ยวกับโครงสร้างที่ใช้อินเทอร์เฟซคือโครงสร้างที่ได้รับการจัดเก็บในตำแหน่งของประเภทอินเทอร์เฟซObjectหรือValueType(ตรงข้ามกับตำแหน่งของประเภทของตัวเอง) จะทำงานเป็นคลาสออบเจ็กต์ สำหรับอินเทอร์เฟซแบบอ่านอย่างเดียวนี่ไม่ใช่ปัญหาโดยทั่วไป แต่สำหรับอินเทอร์เฟซที่กลายพันธุ์เช่นIEnumerator<T>มันสามารถให้ความหมายแปลก ๆ ได้

ตัวอย่างเช่นพิจารณารหัสต่อไปนี้:

List<String> myList = [list containing a bunch of strings]
var enumerator1 = myList.GetEnumerator();  // Struct of type List<String>.IEnumerator
enumerator1.MoveNext(); // 1
var enumerator2 = enumerator1;
enumerator2.MoveNext(); // 2
IEnumerator<string> enumerator3 = enumerator2;
enumerator3.MoveNext(); // 3
IEnumerator<string> enumerator4 = enumerator3;
enumerator4.MoveNext(); // 4

คำสั่งที่ทำเครื่องหมาย # 1 จะเป็นไพรม์enumerator1เพื่ออ่านองค์ประกอบแรก enumerator2รัฐแจงนับที่จะถูกคัดลอกไป ทำเครื่องหมายคำสั่ง # 2 จะได้เลื่อนสำเนาที่จะอ่านองค์ประกอบที่สอง enumerator1แต่จะไม่ส่งผลกระทบต่อ จากนั้นสถานะของตัวนับตัวที่สองจะถูกคัดลอกไปenumerator3ซึ่งจะอยู่ในขั้นสูงโดยทำเครื่องหมายคำสั่ง # 3 จากนั้นก็เพราะenumerator3และenumerator4มีทั้งประเภทของการอ้างอิงการอ้างอิงไปenumerator3แล้วจะถูกคัดลอกไปenumerator4ดังนั้นคำสั่งการทำเครื่องหมายได้อย่างมีประสิทธิภาพจะได้เลื่อนทั้งสอง และenumerator3enumerator4

บางคนพยายามแสร้งทำเป็นว่าประเภทค่าและประเภทอ้างอิงเป็นทั้งสองประเภทObjectแต่นั่นไม่เป็นความจริง ประเภทมูลค่าจริงสามารถแปลงเป็นObjectได้ แต่ไม่ใช่อินสแตนซ์ของมัน อินสแตนซ์List<String>.Enumeratorที่ถูกเก็บไว้ในตำแหน่งของประเภทนั้นคือประเภทค่าและทำงานเป็นประเภทค่า คัดลอกไปยังสถานที่ตั้งของประเภทIEnumerator<String>จะแปลงเป็นชนิดการอ้างอิงและมันจะทำงานเป็นชนิดการอ้างอิง หลังเป็นชนิดหนึ่งObjectแต่อดีตไม่ใช่

BTW หมายเหตุเพิ่มเติมอีกสองสามข้อ: (1) โดยทั่วไปประเภทคลาสที่เปลี่ยนแปลงได้ควรมีEqualsวิธีการทดสอบความเท่าเทียมกันของการอ้างอิง แต่ไม่มีวิธีที่ดีสำหรับโครงสร้างแบบบรรจุกล่องที่จะทำเช่นนั้น (2) แม้จะมีชื่อValueTypeเป็นประเภทคลาสไม่ใช่ประเภทค่า ทุกประเภทที่ได้มาจากSystem.Enumเป็นประเภทค่าเช่นเดียวกับทุกประเภทที่มาจากValueTypeยกเว้นSystem.Enumแต่ทั้งสองประเภทValueTypeและSystem.Enumเป็นประเภทคลาส


3

โครงสร้างถูกนำไปใช้เป็นประเภทค่าและคลาสเป็นประเภทอ้างอิง หากคุณมีตัวแปรประเภท Foo และคุณเก็บอินสแตนซ์ของ Fubar ไว้ในนั้นมันจะ "ใส่กล่อง" เป็นประเภทการอ้างอิงดังนั้นจึงเอาชนะข้อได้เปรียบของการใช้โครงสร้างในตอนแรก

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


มันทำงานแบบนี้สำหรับ primitives ที่ใช้อินเทอร์เฟซด้วยหรือไม่?
aoetalks

3

(ไม่มีอะไรสำคัญที่จะเพิ่ม แต่ยังไม่มีความสามารถในการแก้ไขดังนั้นต่อไปนี้ .. )
Perfectly Safe ไม่มีสิ่งใดผิดกฎหมายกับการนำอินเทอร์เฟซไปใช้กับโครงสร้าง อย่างไรก็ตามคุณควรตั้งคำถามว่าทำไมคุณถึงต้องการทำ

อย่างไรก็ตาม การได้รับการอ้างอิงอินเทอร์เฟซไปยังโครงสร้างจะ BOXมัน โทษประสิทธิภาพและอื่น ๆ

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


หากส่งผ่านInt32ไปยังเมธอดที่ยอมรับประเภททั่วไปT:IComparable<Int32>(ซึ่งอาจเป็นพารามิเตอร์ประเภททั่วไปของเมธอดหรือคลาสของเมธอด) เมธอดนั้นจะสามารถใช้Compareเมธอดบนอ็อบเจ็กต์ที่ส่งผ่านโดยไม่ต้องต่อยมวย
supercat

1

ฉันคิดว่าปัญหาคือมันทำให้เกิดการชกมวยเนื่องจากโครงสร้างเป็นประเภทมูลค่าจึงมีโทษประสิทธิภาพเล็กน้อย

ลิงค์นี้ชี้ให้เห็นว่าอาจมีปัญหาอื่น ๆ ...

http://blogs.msdn.com/abhinaba/archive/2005/10/05/477238.aspx


0

โครงสร้างที่ใช้อินเทอร์เฟซจะไม่มีผล ตัวอย่างเช่นโครงสร้างระบบในตัวใช้อินเทอร์เฟซเช่นIComparableและIFormattable.


0

มีเหตุผลน้อยมากที่ประเภทค่าจะใช้อินเทอร์เฟซ เนื่องจากคุณไม่สามารถ subclass ประเภทค่าได้คุณจึงสามารถอ้างถึงเป็นประเภทคอนกรีตได้เสมอ

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

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


บ่อยแค่ไหนที่คุณผ่าน IComparable แทนการนำไปใช้อย่างเป็นรูปธรรม?
FlySwat

คุณไม่จำเป็นต้องส่งผ่านIComparableเพื่อกำหนดค่า เพียงแค่เรียกเมธอดที่คาดว่าจะIComparableมีประเภทค่าที่นำมาใช้คุณจะใส่กล่องประเภทค่าโดยปริยาย
Andrew Hare

1
@AndrewHare: ยาชื่อสามัญที่มีข้อ จำกัด อนุญาตให้IComparable<T>มีการเรียกใช้วิธีการในโครงสร้างประเภทTโดยไม่ต้องชกมวย
supercat

-10

โครงสร้างก็เหมือนกับคลาสที่อยู่ในสแต็ก ฉันไม่เห็นเหตุผลว่าทำไมจึงควร "ไม่ปลอดภัย"


ยกเว้นพวกเขาไม่มีมรดก
FlySwat

7
ฉันต้องไม่เห็นด้วยกับทุกส่วนของคำตอบนี้ พวกเขาไม่จำเป็นต้องอยู่บนสแต็กและความหมายของการคัดลอกนั้นแตกต่างจากคลาสมาก
Marc Gravell

1
พวกเขาไม่เปลี่ยนรูปการใช้โครงสร้างมากเกินไปจะทำให้ความทรงจำของคุณเศร้า :(
Teoman shipahi

1
@Teomanshipahi การใช้อินสแตนซ์คลาสมากเกินไปจะทำให้คนเก็บขยะของคุณคลั่ง
IllidanS4 รองรับ Monica

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