อนุญาตให้ใช้งานส่วนติดต่ออย่างชัดเจนเพื่อซ่อนสมาชิกใน C # ได้หรือไม่


14

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

public interface IMyInterface
{
    int SomeValue { get; set; }
    int AnotherValue { get; set; }
    bool SomeFlag { get; set; }

    event EventHandler SomeValueChanged;
    event EventHandler AnotherValueChanged;
    event EventHandler SomeFlagChanged;
}

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


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

1
ฉันทำงานกับผู้ชายที่ทำสิ่งนี้เป็นประจำ มันทำให้ฉันถั่วอย่างแน่นอน
Joel Etherton

... และเริ่มใช้การรวม
mikalai

คำตอบ:


39

ใช่มันเป็นรูปแบบที่ไม่ดีโดยทั่วไป

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

ถ้า IntelliSense ของคุณรกเกินไปคลาสของคุณใหญ่เกินไป หากชั้นเรียนของคุณใหญ่เกินไปอาจเป็นไปได้ว่ามีหลายสิ่งหลายอย่างและ / หรือออกแบบมาไม่ดี

แก้ไขปัญหาไม่ใช่อาการของคุณ


มีความเหมาะสมที่จะใช้เพื่อลบคำเตือนคอมไพเลอร์เกี่ยวกับสมาชิกที่ไม่ได้ใช้หรือไม่
Gusdor

11
"แก้ไขปัญหาไม่ใช่อาการของคุณ" +1
FreeAsInBeer

11

ใช้คุณสมบัติสำหรับสิ่งที่ตั้งใจไว้ การลดความยุ่งเหยิงของเนมสเปซไม่ใช่หนึ่งในนั้น

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

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

อีกครั้งฉันจะไม่ใช้มันเพื่อหลบซ่อนหรือจัดระเบียบ มีวิธีในการซ่อนสมาชิกจาก Intellisense

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

"มีผู้เขียน / ผู้เชี่ยวชาญที่โดดเด่นที่คิดว่ามันเป็นคุณสมบัติที่น่าสนใจ" ไคร? ทางเลือกที่เสนอคืออะไร? มีปัญหาอย่างน้อยหนึ่งรายการ (เช่นความเชี่ยวชาญของวิธีการอินเทอร์เฟซในคลาสย่อย / การนำไปใช้) ที่จะต้องเพิ่มคุณสมบัติอื่น ๆ ลงใน C # เพื่อที่จะแก้ปัญหาในกรณีที่ไม่มีการใช้งานที่ชัดเจน (ดู ADO.NET และคอลเลกชันทั่วไป) .
jhominal

@jhominal - CLR ผ่าน C # โดย Jeffrey Richter ใช้คำว่า kludge โดยเฉพาะ C ++ เข้ากันได้โดยไม่ต้องโปรแกรมเมอร์ต้องอ้างอิงอย่างชัดเจนถึงการใช้วิธีการพื้นฐานเพื่อแก้ความกำกวมหรือใช้นักแสดง ฉันได้พูดไปแล้วว่าทางเลือกนั้นไม่น่าดึงดูดนัก แต่มันเป็นแง่มุมของการสืบทอดและอินเทอร์เฟซเดียวไม่ใช่ OOP โดยทั่วไป
codenheim

คุณอาจกล่าวได้ว่า Java ไม่มีการใช้งานที่ชัดเจนทั้งๆที่มีการออกแบบ "การสืบทอดเดี่ยวของการใช้งาน + อินเตอร์เฟส" ที่เหมือนกัน อย่างไรก็ตามใน Java ประเภทการคืนค่าของวิธีการ overriden สามารถเป็นพิเศษโดยการตัดส่วนหนึ่งของความต้องการสำหรับการใช้งานที่ชัดเจน (ใน C # มีการตัดสินใจในการออกแบบที่แตกต่างกันอาจเป็นส่วนหนึ่งเนื่องจากการรวมคุณสมบัติ)
jhominal

@hominal - แน่นอนเมื่อฉันมีไม่กี่นาทีหลังจากนั้นฉันจะอัปเดต ขอบคุณ
codenheim

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

5

ฉันอาจเป็นสัญญาณของรหัสยุ่ง แต่บางครั้งโลกแห่งความจริงก็ยุ่ง ตัวอย่างหนึ่งจาก. NET Framework คือReadOnlyColectionซึ่งซ่อน setter mutating [], Add and Set

IE ReadOnlyCollection ใช้ IList <T> ดูคำถามของฉัน เมื่อหลายปีก่อนด้วยเมื่อฉันไตร่ตรองสิ่งนี้


เป็นอินเทอร์เฟซของฉันเองฉันจะใช้เช่นกันดังนั้นจึงไม่มีเหตุผลที่จะทำผิดตั้งแต่ต้น
Kyle Baran

2
ฉันจะไม่บอกว่ามันซ่อน setter ที่กลายพันธุ์ไว้ แต่มันไม่ได้ให้อะไรเลย ตัวอย่างที่ดีกว่าConcurrentDictionaryคือการดำเนินการกับสมาชิกที่หลากหลายของIDictionary<T>อย่างชัดเจนเพื่อให้ผู้บริโภคของConcurrentDictionaryA) จะไม่เรียกพวกเขาโดยตรงและ B) สามารถใช้วิธีการที่จำเป็นต้องมีการดำเนินการIDictionary<T>หากจำเป็นอย่างยิ่ง เช่นผู้ใช้ConcurrentDictionary<T> ควรจะเรียกTryAddมากกว่าAddเพื่อหลีกเลี่ยงการต้องข้อยกเว้นที่ไม่จำเป็น
ไบรอัน

แน่นอนการใช้งานAddอย่างชัดเจนยังช่วยลดความยุ่งเหยิง TryAddสามารถทำสิ่งที่Addสามารถทำได้ ( Addถูกนำมาใช้เป็นการเรียกTryAddเพื่อให้ทั้งสองจะซ้ำซ้อน) อย่างไรก็ตามTryAddจำเป็นต้องมีชื่อ / ลายเซ็นที่แตกต่างกันดังนั้นจึงเป็นไปไม่ได้ที่จะใช้งานIDictionaryหากไม่มีความซ้ำซ้อนนี้ การใช้งานอย่างชัดเจนช่วยแก้ไขปัญหานี้ได้อย่างหมดจด
Brian

ดีจากมุมมองของผู้บริโภควิธีการที่ถูกซ่อนอยู่
Esben Skov Pedersen

1
คุณเขียนเกี่ยวกับ IReadonlyCollection <T> แต่ลิงก์ไปยัง ReadOnlyCollection <T> ที่แรกในอินเทอร์เฟซที่สองคือคลาส IReadonlyCollection <T> ไม่ได้ใช้ IList <T> แม้ว่า ReadonlyCollection <T> จะใช้งานได้ก็ตาม
Jørgen Fogh

2

เป็นรูปแบบที่ดีที่จะทำเมื่อสมาชิกไม่มีจุดหมายในบริบท ตัวอย่างเช่นหากคุณสร้างคอลเลกชันแบบอ่านอย่างเดียวที่ดำเนินการIList<T>โดยมอบหมายให้กับวัตถุภายใน_wrappedคุณอาจมีลักษณะดังนี้:

public T this[int index]
{
  get
  {
     return _wrapped[index];
  }
}
T IList<T>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
public int Count
{
  get { return _wrapped.Count; }
}
bool ICollection<T>.IsReadOnly
{
  get
  {
    return true;
  }
}

ที่นี่เรามีสี่กรณีที่แตกต่างกัน

public T this[int index]ถูกกำหนดโดยคลาสของเราแทนที่จะเป็นอินเตอร์เฟสและด้วยเหตุนี้จึงไม่ใช่การใช้งานที่ชัดเจนแม้ว่าจะทราบว่ามันจะคล้ายกับการอ่าน - เขียนที่T this[int index]กำหนดในอินเตอร์เฟส แต่เป็นแบบอ่านอย่างเดียว

T IList<T>.this[int index]มีความชัดเจนเนื่องจากส่วนหนึ่งของมัน (ผู้ทะเยอทะยาน) ได้รับการจับคู่อย่างสมบูรณ์แบบโดยคุณสมบัติด้านบนและส่วนอื่น ๆ จะทำให้เกิดข้อยกเว้น ในขณะที่มีความสำคัญต่อคนที่เข้าถึงอินสแตนซ์ของคลาสนี้ผ่านอินเทอร์เฟซก็ไม่มีประโยชน์ที่ใครบางคนใช้มันผ่านตัวแปรประเภทของคลาส

ในทำนองเดียวกันเพราะbool ICollection<T>.IsReadOnlyจะกลับมาจริงเสมอมันไม่มีจุดหมายอย่างเต็มที่กับรหัสที่เขียนกับประเภทของคลาส แต่อาจมีความสำคัญต่อการใช้มันผ่านประเภทของอินเตอร์เฟสดังนั้นเราจึงใช้มันอย่างชัดเจน

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

แต่ด้วยกรณี "ที่ไม่ค่อยได้ใช้งาน" ของคุณฉันจะเอนตัวไปอย่างมากโดยไม่ใช้การดำเนินการที่ชัดเจน

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


หากคุณกำลังใช้อินเทอร์เฟซให้ใช้อินเทอร์เฟซ มิฉะนั้นนี่เป็นการละเมิดหลักการแทนที่ Liskov อย่างชัดเจน
Telastyn

@Telastyn มีการใช้งานอินเตอร์เฟสอย่างแน่นอน
Jon Hanna

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

1
@Telastyn เป็นไปตามสัญญาที่ทำเป็นเอกสารโดยเฉพาะอย่างยิ่งในแง่ของ "คอลเล็กชันที่เป็นแบบอ่านอย่างเดียวไม่อนุญาตให้มีการเพิ่มการลบหรือการดัดแปลงองค์ประกอบหลังจากสร้างคอลเลกชันแล้ว" ดูmsdn.microsoft.com/en-us/library/0cfatk9t%28v=vs.110%29.aspx
Jon Hanna

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