เหตุใด C # จึงไม่มีคำหลัก 'เพื่อน' สไตล์ C ++ [ปิด]


208

เพื่อนคำหลัก C ++ช่วยให้ผู้class Aที่จะกำหนดให้class Bเป็นเพื่อนของตน นี้จะช่วยให้Class Bการเข้าถึงprivate/ สมาชิกของprotectedclass A

ฉันไม่เคยอ่านอะไรเลยว่าทำไมสิ่งนี้ถึงถูกทิ้งไว้ใน C # (และ VB.NET) คำตอบส่วนใหญ่ของคำถาม StackOverflow ก่อนหน้านี้ดูเหมือนจะบอกว่าเป็นส่วนที่มีประโยชน์ของ C ++ และมีเหตุผลที่ดีที่จะใช้ จากประสบการณ์ของฉันฉันต้องยอมรับ

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

หนังสือรูปแบบการออกแบบดั้งเดิมใช้เป็นประจำตลอดตัวอย่าง

สรุปแล้วทำไมfriendหายไปจาก C # และอะไรคือ "วิธีปฏิบัติที่ดีที่สุด" (หรือวิธี) ในการจำลองใน C #

(โดยวิธีการที่internalคำหลักไม่ได้ในสิ่งเดียวกันจะช่วยให้ทุกชั้นเรียนภายในทั้งการชุมนุมในการเข้าถึงinternalสมาชิกในขณะที่friendช่วยให้คุณสามารถที่จะให้ชั้นหนึ่งการเข้าถึงที่สมบูรณ์ไปอีกหนึ่งระดับอื่น ๆ )


5
ฉันรู้สึกว่าการป้องกันภายในเป็นสิ่งประนีประนอมที่ดี ..
Gulzar Nazim

3
มันไม่สับสนเกินไปใช่ไหม? อย่างที่ฉันได้กล่าวไว้ข้างต้นหนังสือ GOF ใช้มันค่อนข้างบ่อยในตัวอย่าง สำหรับฉันแล้วมันไม่ได้ทำให้เกิดความสับสนภายใน
Ash

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

2
ตอบง่าย: C # เสนอ "internal" เป็นตัวดัดแปลงการเข้าถึงซึ่งให้สิทธิ์การเข้าถึงรหัสทั้งหมดในโมดูล / ชุดประกอบเดียวกัน สิ่งนี้ทำให้ไม่ต้องมีอะไรเหมือนเพื่อน ใน Java คำว่า "ป้องกัน" จะทำหน้าที่คล้ายกันในการเข้าถึง wrt จากแพ็คเกจเดียวกัน
sellibitze

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

คำตอบ:


67

การมีเพื่อนในการเขียนโปรแกรมจะถือว่า "สกปรก" มากกว่าหรือน้อยกว่าและไม่เหมาะสม มันแบ่งความสัมพันธ์ระหว่างชั้นเรียนและทำลายคุณสมบัติพื้นฐานบางอย่างของภาษา OO

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

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

แก้ไขให้ฉันอธิบายว่าคำหลักเพื่อนทำลาย OOP อย่างไร

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


167
C # 'ภายใน' กำลังทำให้ encapsulation เจ็บมากกว่า C ++ 'เพื่อน' ด้วย "มิตรภาพ" เจ้าของจะให้สิทธิ์กับใครก็ตามที่ต้องการอย่างชัดเจน "Internal" เป็นแนวคิดปลายเปิด
Nemanja Trifunovic

21
แอนเดอร์ควรได้รับการยกย่องสำหรับคุณลักษณะที่เขาไม่ได้รวมไว้ในที่อื่น
Mehrdad Afshari

21
ฉันไม่เห็นด้วยที่ C # เจ็บภายใน incapsulation แม้ว่า ตรงกันข้ามในความคิดของฉัน คุณสามารถใช้มันเพื่อสร้างระบบนิเวศที่ห่อหุ้มภายในแอสเซมบลี จำนวนวัตถุสามารถแชร์ชนิดภายในภายในระบบนิเวศนี้ที่ไม่ได้สัมผัสกับส่วนที่เหลือของแอปพลิเคชัน ฉันใช้กลอุบายประกอบ "เพื่อน" เพื่อสร้างชุดทดสอบหน่วยสำหรับวัตถุเหล่านี้
Quibblesome

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

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

104

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

ดูข้อความของKonrad Rudolphในเธรดอื่นหรือถ้าคุณต้องการดูรายการที่เกี่ยวข้องใน C ++ คำถามที่พบบ่อย


... และรายการที่เกี่ยวข้องใน FQA: yosefk.com/c++fqa/friend.html#fqa-14.2
Josh Lee

28
+1 สำหรับ " การใช้เพื่อนไม่ได้เกี่ยวกับการละเมิด encapsulation แต่ในทางตรงกันข้ามมันเกี่ยวกับการบังคับใช้ "
Nawaz

2
ฉันคิดว่าเป็นเรื่องของความหมายทางความหมาย และอาจเกี่ยวข้องกับสถานการณ์ที่เป็นปัญหา ทำให้ชั้นเรียนfriendให้สิทธิ์การเข้าถึงสมาชิกส่วนตัวทั้งหมดของคุณแม้ว่าคุณจะต้องให้หนึ่งในนั้นก็ตาม มีการโต้แย้งอย่างสูงว่าการทำให้ฟิลด์พร้อมใช้งานสำหรับคลาสอื่นที่ไม่ต้องการให้นับเป็น "การละเมิด encapsulation" มีสถานที่ใน C ++ ที่จำเป็น (ผู้ประกอบการมากไป) แต่มีการแลกเปลี่ยนแบบห่อหุ้มที่จำเป็นต้องพิจารณาอย่างรอบคอบ ดูเหมือนว่านักออกแบบ C # รู้สึกว่าการแลกเปลี่ยนไม่คุ้มค่า
GrandOpener

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

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

50

สำหรับข้อมูลสิ่งอื่นที่เกี่ยวข้อง แต่ไม่ได้ค่อนข้างเหมือนกันใน. NET คือ[InternalsVisibleTo]ซึ่งช่วยให้แอสเซมบลีกำหนดแอสเซมบลีอื่น (เช่นแอสเซมบลีทดสอบหน่วย) ที่ (อย่างมีประสิทธิภาพ) มีการเข้าถึง "ภายใน" เพื่อประเภท / สมาชิกใน การชุมนุมเดิม


3
คำใบ้ที่ดีบ่อยครั้งที่คนที่มองหาเพื่อนต้องการหาวิธีทดสอบหน่วยด้วยความสามารถในการมี 'ความรู้' ภายในมากขึ้น
SwissCoder

14

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

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


จุดดี. คุณไม่ได้มีโอกาสที่จะมองไปที่คำถาม SO ก่อนหน้านี้ (ถามโดยมอนนอกไซด์และเชื่อมโยงดังกล่าว ?. ฉันจะสนใจในวิธีการที่ดีที่สุดที่จะแก้ปัญหาที่ใน C #?
แอช

ฉันไม่แน่ใจว่าจะทำอย่างไรใน C # แต่ฉันคิดว่าคำตอบของ Jon Skeet สำหรับคำถามของ monoxide ชี้ไปในทิศทางที่ถูกต้อง C # อนุญาตให้เรียนที่ซ้อนกัน ฉันยังไม่ได้ลองรหัสออกและรวบรวมวิธีแก้ไขปัญหา :)
Parappa

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

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

1
ต่อไปนี้เป็นสิ่งหนึ่ง: stackoverflow.com/questions/5921612/… เนื่องจากฉันมาจากพื้นหลัง C ++ การขาดเพื่อนทำให้ฉันเสียสติเกือบทุกวัน ในไม่กี่ปีที่ฉันใช้ C # ฉันได้เปิดเผยวิธีการสาธารณะหลายร้อยวิธีที่พวกเขาสามารถเป็นส่วนตัวและปลอดภัยกว่าแทนทั้งหมดนี้เกิดจากการขาดเพื่อน โดยส่วนตัวแล้วฉันคิดว่าเพื่อนเป็นสิ่งที่สำคัญที่สุดที่ควรเพิ่มใน. NET
kaalus

14

ด้วยfriendนักออกแบบ C ++ สามารถควบคุมได้อย่างแม่นยำว่าใครเป็นสมาชิกส่วนตัว * แต่เขาถูกบังคับให้เปิดเผยสมาชิกส่วนตัวทุกคน

กับ internalนักออกแบบ C # สามารถควบคุมกลุ่มสมาชิกส่วนตัวที่เขาเปิดเผยได้อย่างแม่นยำ เห็นได้ชัดว่าเขาสามารถเปิดเผยเพียงสมาชิกส่วนตัวเดียว แต่จะได้รับการเรียนในชั้นเรียนทั้งหมดในการชุมนุม

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

อย่างที่คุณเห็นเรามี 2 มิติตามที่สามารถหุ้มห่อหุ้มได้ friendละเมิดมันไปอีกมิติหนึ่งinternalไม่ได้ตามแนวอื่น ๆ สิ่งใดที่เป็นสิ่งที่เลวร้ายยิ่งในแนวคิดการห่อหุ้ม? ยากที่จะพูด. แต่มันจะดีถ้ามีทั้งให้friendและinternalพร้อมใช้งาน นอกจากนี้การเพิ่มที่ดีให้กับทั้งสองนี้จะเป็นประเภทที่สามของคำหลักซึ่งจะใช้บนพื้นฐานของสมาชิกโดยสมาชิก (เช่นinternal) และระบุระดับเป้าหมาย (เช่นfriend)

* เพื่อความกระชับฉันจะใช้ "ส่วนตัว" แทน "ส่วนตัวและ / หรือได้รับการป้องกัน"

- นิค


13

ในความเป็นจริง C # ให้ความเป็นไปได้ที่จะได้รับพฤติกรรมเดียวกันในวิธี OOP บริสุทธิ์โดยไม่ต้องใช้คำพิเศษ - มันเป็นอินเตอร์เฟซส่วนตัว

เท่าที่คำถามเพื่อนคืออะไรเทียบเท่า C #? ถูกทำเครื่องหมายว่าซ้ำกับบทความนี้และไม่มีใครเสนอการก่อให้เกิดผลดีจริง ๆ - ฉันจะแสดงคำตอบสำหรับคำถามทั้งสองที่นี่

แนวคิดหลักเกิดขึ้นจากที่นี่: อินเทอร์เฟซส่วนตัวคืออะไร

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

ฉันคิดว่าตัวอย่างที่ดีในการใช้งานจริงอาจเป็นรูปแบบ Full State Machine ซึ่งคอนโทรลเลอร์บางตัวอัพเดตออบเจ็กต์สถานะปัจจุบันและเปลี่ยนเป็นออบเจ็กต์สถานะอื่นเมื่อจำเป็น

คุณสามารถ:

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

Controller.cs

public class Controller
{
    private interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() {  }
    }

    public Controller()
    {
        //it's only way call Update is to cast obj to IState
        IState obj = new StateBase();
        obj.Update();
    }
}

Program.cs

class Program
{
    static void Main(string[] args)
    {
        //it's impossible to write Controller.IState p = new Controller.StateBase();
        //Controller.IState is hidden
        var p = new Controller.StateBase();
        //p.Update(); //is not accessible
    }
}

แล้วมรดกล่ะ

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

Controller.cs

public class Controller
{
    protected interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() { OnUpdate(); }
        protected virtual void OnUpdate()
        {
            Console.WriteLine("StateBase.OnUpdate()");
        }
    }

    public Controller()
    {
        IState obj = new PlayerIdleState();
        obj.Update();
    }
}

PlayerIdleState.cs

public class PlayerIdleState: Controller.StateBase
{
    protected override void OnUpdate()
    {
        base.OnUpdate();
        Console.WriteLine("PlayerIdleState.OnUpdate()");
    }
}

และสุดท้ายตัวอย่างวิธีการทดสอบคลาส Controller โยนมรดก: ControllerTest.cs

class ControllerTest: Controller
{
    public ControllerTest()
    {
        IState testObj = new PlayerIdleState();
        testObj.Update();
    }
}

หวังว่าฉันจะครอบคลุมทุกกรณีและคำตอบของฉันมีประโยชน์


นี่เป็นคำตอบที่ยอดเยี่ยมที่ต้องมีผู้ลงคะแนนมากขึ้น
KthProg

@max_cn นี่เป็นทางออกที่ดีจริงๆ แต่ฉันไม่เห็นวิธีที่จะทำให้การทำงานกับกรอบการเยาะเย้ยสำหรับการทดสอบหน่วย ข้อเสนอแนะใด ๆ
Steve Land

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

@Steve Land คุณสามารถใช้การสืบทอดตามที่แสดงใน ControllerTest.cs เพิ่มวิธีการสาธารณะที่จำเป็นในคลาสการทดสอบที่ได้รับและคุณจะสามารถเข้าถึงเจ้าหน้าที่ที่ได้รับความคุ้มครองทั้งหมดที่คุณมีในคลาสพื้นฐานได้
max_cn

@AnthonyMonterrosa เรากำลังซ่อนอัปเดตจากแหล่งข้อมูลภายนอกทั้งหมดดังนั้นทำไมคุณต้องการแบ่งปันกับใครบางคน;)? แน่นอนว่าคุณสามารถเพิ่มวิธีสาธารณะเพื่อเข้าถึงสมาชิกส่วนตัว แต่มันจะเป็นเหมือนแบ็คดอร์สำหรับชั้นเรียนอื่น ๆ อย่างไรก็ตามหากคุณต้องการใช้สำหรับการทดสอบ - ใช้การสืบทอดเพื่อสร้างคลาสอย่าง ControllerTest เพื่อสร้างเคสการทดสอบที่นั่น
max_cn

9

เพื่อนมีประโยชน์อย่างยิ่งเมื่อเขียนการทดสอบหน่วย

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

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


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

9

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

การพูดว่า "เพื่อนในชั้นเรียนไม่ดี" นั้นสั้นมากเช่นเดียวกับข้อความที่ไม่มีเงื่อนไขอื่น ๆ เช่น "อย่าใช้ gotos" หรือ "Linux ดีกว่า Windows"

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


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

1
@Edward Robertson การทำลายล้างแบบกำหนดไม่ได้ใช้เวลาใด ๆ เว้นแต่จะมีการกำหนด destructor แต่ในกรณีนี้การรวบรวมขยะนั้นแย่กว่านั้นมากเพราะคุณต้องใช้เครื่องมือตัดรอบสุดท้ายซึ่งช้ากว่าและไม่ระบุชื่อ
kaalus

1
... และหน่วยความจำไม่ใช่ทรัพยากรชนิดเดียว ผู้คนมักทำตัวเหมือนเป็นเรื่องจริง
cubuspl42

8

คุณสามารถได้ใกล้ชิดกับ C ++ "เพื่อน" กับ # คำหลักซี"ภายใน"


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

3
@Ash: คุณจำเป็นต้องคุ้นเคยกับมัน แต่เมื่อคุณเห็นส่วนประกอบเป็นส่วนประกอบพื้นฐานของแอปพลิเคชันของคุณinternalทำให้มีความรู้สึกมากขึ้นและให้การแลกเปลี่ยนที่ยอดเยี่ยมระหว่างการห่อหุ้มและยูทิลิตี้ การเข้าถึงค่าเริ่มต้นของ Java นั้นดียิ่งขึ้นกว่าเดิม
Konrad Rudolph

7

นี่ไม่ใช่ปัญหาของ C # มันเป็นข้อ จำกัด พื้นฐานใน IL C # ถูก จำกัด ด้วยสิ่งนี้เช่นเดียวกับภาษา. Net อื่น ๆ ที่พยายามตรวจสอบได้ ข้อ จำกัด นี้ยังรวมคลาสที่ได้รับการจัดการที่กำหนดไว้ใน C ++ / CLI (ข้อมูลจำเพาะส่วน 20.5 )

ที่ถูกกล่าวว่าฉันคิดว่าเนลสันมีคำอธิบายที่ดีว่าทำไมสิ่งนี้เป็นสิ่งที่ไม่ดี


7
-1 friendไม่ใช่สิ่งเลวร้าย internalในทางตรงกันข้ามมันจะดีกว่า และคำอธิบายของเนลสันนั้นไม่ดีและไม่ถูกต้อง
นาวาซ

4

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

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

ภาษาการเขียนโปรแกรมไม่สมบูรณ์แบบ C # เป็นหนึ่งในภาษาที่ดีที่สุดที่ฉันเคยเห็น แต่การแก้ตัวที่โง่ ๆ สำหรับคุณสมบัติที่ขาดหายไปนั้นไม่ได้ช่วยใครเลย ใน C ++ ฉันคิดถึงระบบเหตุการณ์ / ผู้ร่วมประชุมอย่างง่ายการสะท้อน (+ de de / serialization อัตโนมัติ) และ foreach แต่ใน C # ฉันพลาดโอเปอร์เรเตอร์ที่เกิน (ใช่บอกฉันต่อไปว่าคุณไม่ต้องการ) พารามิเตอร์เริ่มต้น const ที่ไม่สามารถหลบเลี่ยงได้รับมรดกหลายอย่าง (ใช่บอกต่อไปเรื่อย ๆ ว่าคุณไม่ต้องการมันและอินเทอร์เฟซนั้นมีการทดแทนอย่างเพียงพอ) และความสามารถในการตัดสินใจที่จะลบอินสแตนซ์จากหน่วยความจำ ประดิษฐ์)


ใน C # คุณสามารถโอเวอร์โหลดโอเปอเรเตอร์ส่วนใหญ่ได้ คุณไม่สามารถโอเวอร์โหลดตัวดำเนินการที่ได้รับมอบหมายได้ แต่ในทางกลับกันคุณสามารถแทนที่||/ &&ในขณะที่ทำให้พวกเขาลัดวงจร มีการเพิ่มพารามิเตอร์เริ่มต้นใน C # 4
CodesInChaos

2

มี InternalsVisibleToAttribute ตั้งแต่. Net 3 แต่ฉันสงสัยว่าพวกเขาจะเพิ่มเฉพาะเพื่อรองรับการทดสอบแอสเซมบลีหลังจากการทดสอบหน่วยเพิ่มขึ้น ฉันไม่เห็นเหตุผลอื่น ๆ อีกมากมายที่จะใช้มัน

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

ค่อนข้างถูกต้องพวกเขาต้องการให้ชุดเพื่อนเป็นกุญแจสำคัญในการป้องกันไม่ให้ใครบางคนสร้างเพื่อนแกล้งข้างการป้องกันของคุณ


2

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

เรามีอะไร (พูดเกี่ยวกับ "เพื่อน" ฉันยังพูดเกี่ยวกับ "ภายใน")

  • การใช้ "เพื่อน" ทำให้โค้ดบริสุทธิ์น้อยลงเกี่ยวกับ oo หรือไม่
  • ใช่;

  • ไม่ใช้ "เพื่อน" ทำให้โค้ดดีขึ้นหรือไม่

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

การใช้เพื่อนทำให้เกิดปัญหาในท้องถิ่นไม่ใช้มันทำให้เกิดปัญหาสำหรับผู้ใช้รหัสห้องสมุด

ทางออกที่ดีทั่วไปสำหรับภาษาการเขียนโปรแกรมฉันเห็นเช่นนี้:

// c++ style
class Foo {
  public_for Bar:
    void addBar(Bar *bar) { }
  public:
  private:
  protected:
};

// c#
class Foo {
    public_for Bar void addBar(Bar bar) { }
}

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


ฉันไม่ใช่ Sentinel ของ OOP แต่ดูเหมือนว่ามันจะช่วยให้ทุกคนจับกับเพื่อนและภายในได้ แต่ฉันขอแนะนำให้ใช้แอตทริบิวต์เพื่อหลีกเลี่ยงการขยายไวยากรณ์ของ C #: [PublicFor Bar] void addBar (แถบบาร์) {} ... ไม่ใช่ว่าฉันแน่ใจว่าเหมาะสม ฉันไม่ทราบว่าคุณลักษณะทำงานอย่างไรหรือว่าพวกเขาสามารถเล่นซอกับความสามารถในการเข้าถึงได้หรือไม่
Grault

2

ฉันจะตอบคำถาม "เท่านั้น"

มีคำตอบมากมายที่นี่ แต่ฉันต้องการเสนอ "รูปแบบการออกแบบ" เพื่อให้ได้คุณลักษณะนั้น ฉันจะใช้กลไกภาษาอย่างง่ายซึ่งรวมถึง:

  • อินเตอร์เฟซ
  • คลาสที่ซ้อนกัน

ตัวอย่างเช่นเรามี 2 ห้องเรียนหลัก: นักศึกษาและมหาวิทยาลัย นักเรียนมีเกรดเฉลี่ยซึ่งมหาวิทยาลัยเท่านั้นที่ได้รับอนุญาตให้เข้าถึง นี่คือรหัส:

public interface IStudentFriend
{
    Student Stu { get; set; }
    double GetGPS();
}

public class Student
{
    // this is private member that I expose to friend only
    double GPS { get; set; }
    public string Name { get; set; }

    PrivateData privateData;

    public Student(string name, double gps) => (GPS, Name, privateData) = (gps, name, new PrivateData(this);

    // No one can instantiate this class, but Student
    // Calling it is possible via the IStudentFriend interface
    class PrivateData : IStudentFriend
    {
        public Student Stu { get; set; }

        public PrivateData(Student stu) => Stu = stu;
        public double GetGPS() => Stu.GPS;
    }

    // This is how I "mark" who is Students "friend"
    public void RegisterFriend(University friend) => friend.Register(privateData);
}

public class University
{
    var studentsFriends = new List<IStudentFriend>();

    public void Register(IStudentFriend friendMethod) => studentsFriends.Add(friendMethod);

    public void PrintAllStudentsGPS()
    {
        foreach (var stu in studentsFriends)
            Console.WriteLine($"{stu.Stu.Name}: stu.GetGPS()");
    }
}

public static void Main(string[] args)
{
    var Technion = new University();
    var Alex     = new Student("Alex", 98);
    var Jo       = new Student("Jo", 91);

    Alex.RegisterFriend(Technion);
    Jo.RegisterFriend(Technion);
    Technion.PrintAllStudentsGPS();

    Console.ReadLine();
}

1

ฉันสงสัยว่ามันมีบางอย่างที่เกี่ยวข้องกับรูปแบบการคอมไพล์ C # - การสร้าง IL โดย JIT กำลังรวบรวมสิ่งนั้นในขณะใช้งานจริง เช่น: เหตุผลเดียวกับที่ C # generics นั้นแตกต่างกับ C ++ ทั่วไป


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

1

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


ถ้าคุณไม่ทำงานกับแอปพลิเคชัน Silverlight
kaalus

1

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

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

เป็นเรื่องงี่เง่าธรรมดาที่จะประกาศว่าเป็นการละเมิด OOP สำหรับคลาสโพรโทคอล / "ผู้สร้าง" เพื่อเข้าถึงคลาสที่สร้างขึ้นอย่างใกล้ชิด - คลาสของผู้สร้างต้องมีการกวาดล้างข้อมูลทุกบิต สิ่งที่ฉันได้พบที่สำคัญที่สุดคือการลดรหัสพิเศษของ BS ให้น้อยที่สุดโดยปกติรูปแบบ "OOP for Sake OOP" จะนำไปสู่ ปาเก็ตตี้เสริมเพียงแค่ทำให้ข้อบกพร่องมากขึ้น

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

หากคุณมีคลาส C ++ ที่ใช้คำหลักเพื่อนและต้องการเลียนแบบในคลาส C #: 1. ประกาศคลาส C # สาธารณะ 2. ประกาศคุณสมบัติ / คุณสมบัติ / วิธีการทั้งหมดที่ได้รับการป้องกันใน C ++ และเพื่อน ๆ สามารถเข้าถึงได้เช่นกัน internal ใน C # 3 สร้างคุณสมบัติอ่านอย่างเดียวสำหรับการเข้าถึงสาธารณะกับคุณสมบัติและคุณสมบัติภายในทั้งหมด

ฉันยอมรับว่ามันไม่เหมือนกับเพื่อน 100% และการทดสอบหน่วยเป็นตัวอย่างที่มีค่ามากสำหรับความต้องการของเพื่อน (เช่นเดียวกับรหัสบันทึกการวิเคราะห์โปรโตคอล) อย่างไรก็ตามภายในให้การเปิดเผยคลาสที่คุณต้องการรับแสงและ [InternalVisibleTo ()] จัดการส่วนที่เหลือ - ดูเหมือนว่ามันเกิดมาโดยเฉพาะสำหรับการทดสอบหน่วย

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


1

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

ตัวอย่างเช่น

interface IFriend { }

class Friend : IFriend
{
    public static IFriend New() { return new Friend(); }
    private Friend() { }

    private void CallTheBody() 
    {  
        var body = new Body();
        body.ItsMeYourFriend(this);
    }
}

class Body
{ 
    public void ItsMeYourFriend(Friend onlyAccess) { }
}

ทั้งๆที่ข้อเท็จจริงว่าItsMeYourFriend()เป็นFriendชั้นเรียนสาธารณะเท่านั้นที่สามารถเข้าถึงได้เนื่องจากไม่มีใครสามารถรับตัวอย่างที่เป็นรูปธรรมของFriendชั้นเรียนได้ มันมีคอนสตรัคเตอร์ส่วนตัวในขณะที่New()วิธีการที่โรงงานส่งกลับอินเตอร์เฟซ

ดูบทความเพื่อนและสมาชิกอินเทอร์เฟซภายในของฉันโดยไม่เสียค่าใช้จ่ายใด


มิตรภาพที่น่าเศร้าไม่สามารถจำลองได้ในทุกวิถีทาง ดูคำถามนี้: stackoverflow.com/questions/5921612/…
kaalus

1

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

ฉันมีเพจ ASP.NET ที่สืบทอดเพจฐานของตัวเองซึ่งจะสืบทอด System.Web.UI.Page ในหน้านี้ฉันมีรหัสบางอย่างที่จัดการการรายงานข้อผิดพลาดของผู้ใช้ปลายทางสำหรับแอปพลิเคชันในวิธีที่ได้รับการป้องกัน

ReportError("Uh Oh!");

ตอนนี้ฉันมีการควบคุมผู้ใช้ที่มีอยู่ในหน้า ฉันต้องการให้ผู้ใช้ควบคุมสามารถเรียกวิธีการรายงานข้อผิดพลาดในหน้า

MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");

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

protected void ReportError(string str) {
    MyBasePage bp = Page as MyBasePage;
    bp.ReportError(str);
}

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

[Friend(B)]
class A {

    AMethod() { }

    [Friend(C)]
    ACMethod() { }
}

class B {
    BMethod() { A.AMethod() }
}

class C {
    CMethod() { A.ACMethod() }
}

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

class BasePage {

    [Friend(BaseControl.ReportError(string)]
    protected void ReportError(string str) { }
}

class BaseControl {
    protected void ReportError(string str) {
        MyBasePage bp = Page as MyBasePage;
        bp.ReportError(str);
    }
}

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


0

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


ฉันเห็นด้วย. ฉันไม่ต้องการคำหลักเพื่อน โดยทั่วไปใช้เพื่อแก้ปัญหาการบำรุงรักษาที่ซับซ้อน
Edouard A.

7
ผู้ประกอบการบรรทุกเกินพิกัดทุกคน ??
เราทุกคนโมนิกา

3
คุณพบกรณีที่ดีสำหรับผู้ให้บริการมากไปอย่างจริงจังกี่ครั้ง
bashmohandes

8
ฉันจะให้กรณีง่ายมากที่หักล้างความคิดเห็น 'ปัญหาการออกแบบ' ของคุณโดยสมบูรณ์ ผู้ปกครองเด็ก ผู้ปกครองเป็นเจ้าของลูก ๆ เด็กในคอลเลกชัน 'Children' ของผู้ปกครองนั้นเป็นของผู้ปกครองนั้น ผู้ตั้งค่าคุณสมบัติหลักของผู้ปกครองในเด็กไม่ควรตั้งค่าโดยใครก็ได้ มันจะต้องได้รับมอบหมายเมื่อและเมื่อเด็กจะถูกเพิ่มลงในคอลเลกชันเด็กของผู้ปกครอง ถ้าเป็นสาธารณะทุกคนสามารถเปลี่ยนได้ ภายใน: ทุกคนในชุดประกอบนั้น เอกชน? ไม่มีใคร. การทำให้สุนัขเซทเทอร์เป็นเพื่อนกับผู้ปกครองช่วยขจัดปัญหาเหล่านั้นและยังทำให้ชั้นเรียนของคุณแยกจากกัน แต่มีความสัมพันธ์กันแน่น Huzzah !!
Mark A. Donohoe

2
มีหลายกรณีเช่นรูปแบบการจัดการ / การจัดการขั้นพื้นฐานส่วนใหญ่ มีคำถามอีกข้อหนึ่งเกี่ยวกับ SO และไม่มีใครคิดวิธีที่จะทำโดยไม่มีเพื่อน ไม่แน่ใจว่าสิ่งที่ทำให้คนคิดว่าชั้นหนึ่ง "จะพอเพียง" บางครั้งมากกว่าหนึ่งชั้นต้องทำงานร่วมกัน
kaalus

0

Bsd

มันบอกว่าเพื่อนเจ็บ OOness บริสุทธิ์ ซึ่งฉันเห็นด้วย

มันยังระบุด้วยว่าเพื่อนช่วย encapsulation ซึ่งฉันก็เห็นด้วย

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

หนึ่งสามารถใช้งานได้ดังต่อไปนี้

    class C1
    {
        private void MyMethod(double x, int i)
        {
            // some code
        }
        // the friend class would be able to call myMethod
        public void MyMethod(FriendClass F, double x, int i)
        {
            this.MyMethod(x, i);
        }
        //my friend class wouldn't have access to this method 
        private void MyVeryPrivateMethod(string s)
        {
            // some code
        }
    }
    class FriendClass
    {
        public void SomeMethod()
        {
            C1 c = new C1();
            c.MyMethod(this, 5.5, 3);
        }
    }

แน่นอนว่าจะสร้างคำเตือนของคอมไพเลอร์และจะทำให้เกิดอันตรายต่อ Intellisense แต่มันจะทำงาน

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

#if UNIT_TESTING
        public
#else
        private
#endif
            double x;

เพื่อให้คุณเขียนรหัสทั้งหมดโดยไม่กำหนด UNIT_TESTING และเมื่อคุณต้องการทดสอบหน่วยคุณเพิ่ม #define UNIT_TESTING ลงในบรรทัดแรกของไฟล์ (และเขียนรหัสทั้งหมดที่ทำการทดสอบหน่วยใน #if UNIT_TESTING) ที่ควรจัดการอย่างระมัดระวัง

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


2
พารามิเตอร์ FriendClass นั้นไม่ได้ทำหน้าที่ควบคุมการเข้าถึง: คุณสามารถโทรc.MyMethod((FriendClass) null, 5.5, 3)จากคลาสใดก็ได้
Jesse McGrew

0

มิตรภาพอาจถูกจำลองโดยใช้ "ตัวแทน" - บางคลาสภายใน ลองพิจารณาตัวอย่างต่อไปนี้:

public class A // Class that contains private members
{
  private class Accessor : B.BAgent // Implement accessor part of agent.
  {
    private A instance; // A instance for access to non-static members.
    static Accessor() 
    { // Init static accessors.
      B.BAgent.ABuilder = Builder;
      B.BAgent.PrivateStaticAccessor = StaticAccessor;
    }
    // Init non-static accessors.
    internal override void PrivateMethodAccessor() { instance.SomePrivateMethod(); }
    // Agent constructor for non-static members.
    internal Accessor(A instance) { this.instance = instance; }
    private static A Builder() { return new A(); }
    private static void StaticAccessor() { A.PrivateStatic(); }
  }
  public A(B friend) { B.Friendship(new A.Accessor(this)); }
  private A() { } // Private constructor that should be accessed only from B.
  private void SomePrivateMethod() { } // Private method that should be accessible from B.
  private static void PrivateStatic() { } // ... and static private method.
}
public class B
{
  // Agent for accessing A.
  internal abstract class BAgent
  {
    internal static Func<A> ABuilder; // Static members should be accessed only by delegates.
    internal static Action PrivateStaticAccessor;
    internal abstract void PrivateMethodAccessor(); // Non-static members may be accessed by delegates or by overrideable members.
  }
  internal static void Friendship(BAgent agent)
  {
    var a = BAgent.ABuilder(); // Access private constructor.
    BAgent.PrivateStaticAccessor(); // Access private static method.
    agent.PrivateMethodAccessor(); // Access private non-static member.
  }
}

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

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