เหตุใดเขตข้อมูลส่วนตัวจึงเป็นประเภทส่วนตัวไม่ใช่เช่นนั้น


153

ใน C # (และภาษาอื่น ๆ อีกมากมาย) การเข้าใช้ฟิลด์ส่วนตัวของอินสแตนซ์อื่นประเภทเดียวกันนั้นถูกต้องตามกฎหมาย ตัวอย่างเช่น:

public class Foo
{
    private bool aBool;

    public void DoBar(Foo anotherFoo)
    {
        if (anotherFoo.aBool) ...
    }
}

เนื่องจากข้อกำหนดC # (ส่วน 3.5.1, 3.5.2) ระบุว่าการเข้าถึงฟิลด์ส่วนตัวอยู่ในประเภทไม่ใช่อินสแตนซ์ ฉันกำลังคุยเรื่องนี้กับเพื่อนร่วมงานและเรากำลังพยายามหาเหตุผลว่าทำไมมันถึงใช้งานได้ (แทนที่จะ จำกัด การเข้าถึงอินสแตนซ์เดียวกัน)

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


18
สิ่งที่เป็นประโยชน์สำหรับทางเลือกคืออะไร?
Jon Skeet

19
มันเป็นความจริงที่มันไม่ได้จำลองโลกแห่งความเป็นจริงได้ดีนัก ท้ายที่สุดเพียงเพราะรถของฉันสามารถรายงานว่ามีก๊าซเหลืออยู่เท่าไรไม่ได้หมายความว่ารถของฉันควรจะสามารถบอกได้ว่ารถทุกคันมีแก๊สเหลืออยู่เท่าไหร่ ใช่ฉันสามารถเห็นว่าในทางทฤษฎีมีการเข้าถึง "อินสแตนซ์ส่วนตัว" ฉันรู้สึกว่าฉันจะไม่ใช้มันเลยเพราะฉันจะเป็นห่วงว่าใน 99% ของกรณีฉันอยากได้อินสแตนซ์อื่นเข้าถึงตัวแปรนั้น
aquinas

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

4
คุณสามารถทำให้ตัวแปรเป็นคู่ของการปิด getter / หมาที่ initialised ในตัวสร้างที่จะล้มเหลวถ้านี้ไม่ได้เช่นเดียวกับที่มันเป็นช่วง initialisation และ ...
มาร์ติน Sojka

8
Scala จัดให้มีสมาชิกส่วนตัวเช่น - พร้อมกับระดับการเข้าถึงอื่น ๆ ที่ไม่พบใน Java, C # และภาษาอื่น ๆ อีกมากมาย นี่เป็นคำถามที่สมบูรณ์แบบ
Bruno Reis

คำตอบ:


73

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

public class Foo
{
    private int bar;

    public void Baz(Foo other)
    {
        other.bar = 2;
    }

    public void Boo()
    {
        Baz(this);
    }
}

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

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

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

อย่างไรก็ตามฉันเชื่อว่าสิ่งนี้ทำให้เกิดความสับสนมากขึ้นกว่าที่ควรจะเป็น ผู้ใช้ส่วนใหญ่ (รวมตัวเอง) จะพบว่ามัน unneccessarily จำกัด ถ้าโค้ดข้างต้นไม่ได้ทำงาน: หลังจากทั้งหมดที่ฉันข้อมูลที่ฉันพยายามที่จะเข้าถึง! ทำไมฉันจึงควรจะต้องไปผ่านthis?

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


33
IMHO เหตุผลนี้เป็นวิธีที่ผิด คอมไพเลอร์บังคับใช้กฎของภาษา มันไม่ได้ทำให้พวกเขา กล่าวอีกนัยหนึ่งหากสมาชิกส่วนตัวเป็น "ส่วนตัวส่วนตัว" และไม่ใช่ "พิมพ์ส่วนตัว" คอมไพเลอร์จะได้รับการดำเนินการในรูปแบบอื่น ๆ เช่นการอนุญาตเท่านั้นthis.bar = 2แต่ไม่other.bar = 2เพราะotherอาจเป็นกรณีที่แตกต่างกัน
Daniel Hilgarth

@Daniel นั่นเป็นจุดที่ดี แต่อย่างที่ฉันพูดไปฉันคิดว่าการ จำกัด นั้นจะแย่กว่าการมองเห็นระดับชั้น
dlev

3
@dlev: ฉันไม่ได้พูดอะไรเกี่ยวกับว่าสมาชิกอินสแตนซ์ส่วนตัวเป็นสิ่งที่ดีหรือไม่ :-) ฉันแค่อยากจะชี้ให้เห็นว่าคุณกำลังโต้เถียงว่าการใช้งานทางเทคนิคกำหนดกฎของภาษาและในความเห็นของฉันการโต้แย้งนี้ไม่ถูกต้อง
Daniel Hilgarth

2
@Daniel Point ถ่ายไว้ ฉันยังคิดว่านี่เป็นการพิจารณาที่ถูกต้องแม้ว่าคุณจะถูกต้องแน่นอนว่าในที่สุดนักออกแบบภาษาจะเข้าใจสิ่งที่พวกเขาต้องการและผู้เขียนคอมไพเลอร์ก็ปฏิบัติตามนั้น
dlev

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

61

เนื่องจากจุดประสงค์ของการห่อหุ้มที่ใช้ในภาษา C # และภาษาที่คล้ายกัน * คือการลดการพึ่งพาซึ่งกันและกันของรหัสที่ต่างกัน (คลาสใน C # และ Java) ไม่ใช่วัตถุที่แตกต่างกันในหน่วยความจำ

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

อย่างไรก็ตามทฤษฎีทั้งหมดเกี่ยวกับการห่อหุ้มล้มเหลวทันทีที่มีคนสร้างคุณสมบัติ (หรือรับ / ตั้งค่าคู่ใน Java) และแสดงฟิลด์ทั้งหมดโดยตรงซึ่งทำให้คลาสเป็นคู่เหมือนกับว่าพวกเขากำลังเข้าถึงฟิลด์

* สำหรับการอธิบายประเภทของการห่อหุ้มดูคำตอบที่ยอดเยี่ยมของ Abel


3
ฉันไม่คิดว่ามันจะล้มเหลวเมื่อget/setมีวิธีการให้ วิธีการเข้าถึงยังคงรักษาสิ่งที่เป็นนามธรรม (ผู้ใช้ไม่จำเป็นต้องรู้ว่ามีฟิลด์อยู่ข้างหลังหรือฟิลด์ใดที่อยู่ด้านหลังคุณสมบัติ) ตัวอย่างเช่นถ้าเราเขียนComplexคลาสเราสามารถแสดงคุณสมบัติเพื่อรับ / ตั้งค่าพิกัดขั้วโลกและอีกหนึ่งรับ / ตั้งค่าสำหรับพิกัดคาร์ทีเซียน ผู้ใช้คนใดในชั้นเรียนที่อยู่ด้านล่าง (อาจเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง)
Ken Wayne VanderLinde

5
@Ken: มันจะล้มเหลวถ้าคุณให้คุณสมบัติสำหรับทุกฟิลด์ที่คุณมีโดยไม่พิจารณาว่าควรจะเปิดเผย
Goran Jovic

@Goran ถึงแม้ว่าฉันจะเห็นด้วยว่ามันจะไม่เหมาะ แต่ก็ยังไม่ได้ล้มเหลวอย่างสมบูรณ์เนื่องจาก access / set accessors อนุญาตให้คุณเพิ่มตรรกะเพิ่มเติมได้หากมีการเปลี่ยนแปลงฟิลด์ต้นแบบ
ForbesLindesay

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

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

49

มีการเพิ่มคำตอบบางส่วนในหัวข้อที่น่าสนใจนี้แล้ว แต่ฉันไม่พบเหตุผลที่แท้จริงว่าทำไมพฤติกรรมนี้จึงเป็นแบบนี้ ให้ฉันลองดู:

ย้อนไปเมื่อวันวาน

อยู่ระหว่าง Smalltalk ใน 80 และ Java ในช่วงกลางยุค 90 แนวคิดของการวางแนววัตถุที่ครบกำหนด การซ่อนข้อมูลไม่ใช่แนวคิดดั้งเดิมที่มีให้เฉพาะกับ OO (กล่าวถึงครั้งแรกในปี 1978) ถูกนำมาใช้ใน Smalltalk เนื่องจากข้อมูลทั้งหมด (สาขา) ของชั้นเรียนมีความเป็นส่วนตัวทุกวิธีเป็นแบบสาธารณะ ในระหว่างการพัฒนาใหม่ของ OO ในยุค 90 เบอร์ทรานด์เมเยอร์พยายามทำให้แนวคิดของ OO เป็นรูปเป็นร่างขึ้นในหนังสือสำคัญของเขาObject Oriented Software Construction (OOSC)ซึ่งนับตั้งแต่นั้นมาก็ถือเป็นการอ้างอิงที่ชัดเจนเกี่ยวกับแนวคิดของ OO และการออกแบบภาษา .

ในกรณีของการมองเห็นส่วนตัว

ตามวิธีการของเมเยอร์ควรมีให้สำหรับชุดคลาสที่กำหนด (หน้า 192-193) สิ่งนี้ทำให้เห็นได้ชัดว่ามีการซ่อนตัวของข้อมูลที่ละเอียดสูงมากคุณลักษณะต่อไปนี้มีให้สำหรับ classA และ classB และการสืบทอดทั้งหมด:

feature {classA, classB}
   methodName

ในกรณีที่privateเขากล่าวว่าต่อไปนี้: โดยไม่ต้องประกาศประเภทที่ชัดเจนในระดับของตัวเองคุณไม่สามารถเข้าถึงคุณลักษณะ (เมธอด / ฟิลด์) ในการโทรที่ผ่านการรับรอง คือถ้าxเป็นตัวแปรx.doSomething()ไม่ได้รับอนุญาต แน่นอนว่าการเข้าถึงที่ไม่ได้รับอนุญาตจะทำได้ภายในชั้นเรียนเอง

กล่าวอีกนัยหนึ่ง: เพื่ออนุญาตการเข้าถึงโดยอินสแตนซ์ของคลาสเดียวกันคุณต้องอนุญาตการเข้าถึงเมธอดของคลาสนั้นอย่างชัดเจน บางครั้งเรียกว่าอินสแตนซ์ส่วนตัวกับคลาสส่วนตัว

อินสแตนซ์ส่วนตัวในภาษาการเขียนโปรแกรม

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

ตัวเลือกสำหรับการออกแบบภาษา

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

จากมุมมองทางเทคนิคไม่มีเหตุผลที่จะเลือกทางเดียวหรืออื่น ๆ (โดยเฉพาะเมื่อพิจารณาว่า Eiffel.NET สามารถทำสิ่งนี้กับ IL ได้แม้จะมีหลายมรดก แต่ก็ไม่มีเหตุผลที่จะให้คุณลักษณะนี้)

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

เหตุใด C # อนุญาตให้มีการห่อหุ้มคลาสเท่านั้นและไม่ใช่การห่อหุ้มอินสแตนซ์

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

อย่างไรก็ตาม C # ดูเป็นเรื่องยากที่สุดในการออกแบบภาษา C ++ และ Java ในขณะที่ Eiffel และ Modula-3 อยู่ในรูปภาพด้วยเมื่อพิจารณาถึงคุณสมบัติหลายอย่างของ Eiffel ที่หายไป (การสืบทอดหลายแบบ) ฉันเชื่อว่าพวกเขาเลือกเส้นทางเดียวกับ Java และ C ++ เมื่อมาถึงตัวปรับการเข้าถึงส่วนตัว

หากคุณต้องการทราบว่าทำไมคุณควรลองจับ Eric Lippert, Krzysztof Cwalina, Anders Hejlsberg หรือใครก็ตามที่ทำงานบนมาตรฐานของ C # แต่น่าเสียดายที่ผมไม่สามารถหาบันทึกที่ชัดเจนในข้อเขียนC # Programming Language


1
นั่นเป็นคำตอบที่น่ารักที่มีพื้นหลังมากมายน่าสนใจมากขอบคุณที่สละเวลาตอบคำถามเก่า ๆ (ค่อนข้าง)
RichK

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

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

2
คำตอบที่ดี OP ควรพิจารณาทำเครื่องหมายคำถามคำตอบนี้
Gavin Osborn

18

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


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

@quinas: มันค่อนข้างแตกต่างกัน การทำให้ทุกอย่างเป็นสาธารณะจะนำไปสู่การฝึกเขียนโปรแกรมที่น่ากลัว การอนุญาตให้ประเภทดูเขตข้อมูลส่วนตัวของอินสแตนซ์อื่นของตัวเองเป็นกรณีที่แคบมาก
Igby Largeman

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

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

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

13

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


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

2
@quinas มันไม่ทำงานอย่างนั้น เขตข้อมูลเป็นเขตข้อมูล พวกเขาเป็นแบบอ่านอย่างเดียวเท่านั้นหากประกาศเป็นreadonlyและจากนั้นจะใช้กับอินสแตนซ์ที่ประกาศเช่นกัน เห็นได้ชัดว่าคุณกำลังยืนยันว่าควรมีกฎคอมไพเลอร์เฉพาะที่ห้ามมิให้กฎนี้ แต่กฎดังกล่าวทุกข้อเป็นคุณลักษณะที่ทีมงาน C # จะต้องระบุระบุเอกสารแปลเป็นภาษาท้องถิ่นทดสอบบำรุงรักษาและให้การสนับสนุน หากไม่มีประโยชน์ที่น่าสนใจแล้วทำไมพวกเขาจะทำอย่างไร
Aaronaught

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

1
ทีม C #? ฉันแค่ถกเถียงถึงการเปลี่ยนแปลงทางทฤษฎีที่เป็นไปได้ทางภาษา "ถ้าไม่มีประโยชน์ที่น่าสนใจ ... " ฉันคิดว่าอาจมีประโยชน์ เช่นเดียวกับการใด ๆรับประกันคอมไพเลอร์บางคนอาจพบว่ามันมีประโยชน์ที่จะรับประกันว่าชั้นเรียนเพียงปรับเปลี่ยนสถานะของตัวเอง
aquinas

@ aquinas: คุณลักษณะต่างๆได้รับการใช้งานเพราะมีประโยชน์ต่อผู้ใช้จำนวนมากหรือส่วนใหญ่ไม่ใช่เพราะอาจมีประโยชน์ในบางกรณี
Aaronaught

9

ก่อนอื่นจะเกิดอะไรขึ้นกับสมาชิกสแตติกส่วนตัว? พวกเขาสามารถเข้าถึงได้โดยวิธีการคงที่? แน่นอนคุณจะไม่ต้องการที่แล้วเพราะคุณจะไม่สามารถเข้าถึงของคุณconsts

สำหรับคำถามที่ชัดเจนของคุณให้พิจารณากรณีของ a StringBuilderซึ่งนำมาใช้เป็นรายการเชื่อมโยงของอินสแตนซ์ของตัวเอง

public class StringBuilder
{
    private string chunk;
    private StringBuilder nextChunk;
}

หากคุณไม่สามารถเข้าถึงสมาชิกส่วนตัวของอินสแตนซ์อื่น ๆ ในชั้นเรียนของคุณคุณต้องดำเนินการToStringดังนี้:

public override string ToString()
{
    return chunk + nextChunk.ToString();
}

สิ่งนี้จะใช้ได้ แต่เป็น O (n ^ 2) - ไม่มีประสิทธิภาพมาก อันที่จริงแล้วนั่นอาจเอาชนะจุดประสงค์ทั้งหมดของการมีStringBuilderชั้นเรียนได้ตั้งแต่แรก หากคุณสามารถเข้าถึงสมาชิกส่วนตัวของอินสแตนซ์อื่นของคลาสของคุณเองคุณสามารถนำไปใช้ToStringโดยการสร้างสตริงที่มีความยาวที่เหมาะสมจากนั้นทำสำเนาที่ไม่ปลอดภัยของแต่ละอันไปยังตำแหน่งที่เหมาะสมในสตริง:

public override string ToString()
{
    string ret = string.FastAllocateString(Length);
    StringBuilder next = this;

    unsafe
    {
        fixed (char *dest = ret)
            while (next != null)
            {
                fixed (char *src = next.chunk)
                    string.wstrcpy(dest, src, next.chunk.Length);
                next = next.nextChunk;
            }
    }
    return ret;
}

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


ใช่ แต่ไม่มีวิธีอื่นในนั้นเช่นการเปิดเผยบางฟิลด์เป็นแบบภายใน?
MikeKulls

2
@Mike: การเปิดเผยฟิลด์เนื่องจากinternalทำให้เป็นส่วนตัวน้อยลง ! คุณจะต้องเผยให้เห็นภายในของชั้นเรียนของคุณกับทุกสิ่งในการชุมนุม
Gabe

3

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

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

มีการสนทนาที่คล้ายกันที่นี่


1

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

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

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

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


0

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

BTW .... ปัญหาเดียวกันนี้ใช้กับสมาชิก "ที่ได้รับการป้องกัน" เช่นกัน :(

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

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


-1

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

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

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