เหตุใดจึงต้องใช้วิธีสาธารณะในชั้นเรียนภายใน


250

มีรหัสจำนวนมากในหนึ่งในโครงการของเราที่มีลักษณะดังนี้:

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (s == null)
        {
            return "Foo";
        }

        return $({s}Foo);
    }
}

มีเหตุผลที่ชัดเจนในการทำสิ่งนี้นอกเหนือจาก "ง่ายกว่าที่จะทำให้เป็นสาธารณะในภายหลัง"

ฉันสงสัยว่ามันสำคัญเฉพาะในกรณีขอบแปลก ๆ (การสะท้อนใน Silverlight) หรือไม่เลย


1
จากประสบการณ์ของฉันนั่นเป็นเรื่องปกติ
phoog

2
@phoog ตกลง แต่ทำไมมันถึงเป็นเรื่องปกติ อาจจะง่ายขึ้นแล้วเปลี่ยนวิธีการต่างๆเป็นแบบภายใน? แก้ไขด่วน -> ทำเครื่องหมายประเภทภายในแทนหรือไม่
bopapa_1979

นั่นคือสิ่งที่ฉันคิดเอาไว้เสมอ ฉันไม่มีการวิเคราะห์ที่ดีอย่างไรก็ตามนี่คือเหตุผลที่ฉันไม่ได้โพสต์คำตอบ
phoog

2
คำตอบของ Eric Lippert สรุปความคิดของฉันได้เป็นอย่างดีและยังนำเสนอบางสิ่งที่ซ่อนอยู่ในสมองของฉันพยายามหาทางออก: การใช้งานโดยนัยของสมาชิกอินเทอร์เฟซต้องเป็นสาธารณะ
phoog

วิธีการนั้นไม่เท่ากับreturn s + "Foo";หรือ? +ผู้ประกอบการไม่สนใจเกี่ยวกับโมฆะหรือสตริงที่ว่างเปล่า
Mikkel R. Lund

คำตอบ:


419

UPDATE: คำถามนี้เป็นเรื่องของการบล็อกของฉันในกันยายน 2014 ขอบคุณสำหรับคำถามที่ยอดเยี่ยม!

มีการถกเถียงกันอย่างมากในคำถามนี้แม้จะอยู่ในทีมคอมไพเลอร์เองก็ตาม

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

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

ความคิดเห็นของฉันคือ: ทำเครื่องหมายสมาชิกเช่นสาธารณะ

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

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

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

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


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

10
+1 สำหรับพูดสิ่งที่ฉันต้องการจะพูด แต่กำหนดให้ดีกว่าที่ฉันสามารถทำได้ (เช่นกันสำหรับการนำมุมอินเทอร์เฟซมาใช้แม้ว่าฉันจะทราบว่ามันเป็นเรื่องจริงของการใช้งานโดยปริยายของสมาชิกเท่านั้น)
phoog

2
ส่วนอินเทอร์เฟซมีความสำคัญ - เป็นไปไม่ได้ที่จะใช้วิธีการอินเทอร์เฟซที่ใช้ภายในเป็นประกาศของอินเตอร์เฟซที่สาธารณะ ดังนั้นคำว่าสาธารณะจึงมีความหมายมากเกินไปสองความหมาย
Michael Stum

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

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

17

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

ในขณะที่บางคนบอกว่าสิ่งนี้ช่วยลดการเปลี่ยนจากinternalเป็นpublicเป็น มันยังทำหน้าที่เป็นส่วนหนึ่งของคำอธิบายของวิธีการ Internalโดยทั่วไปแล้ววิธีการนั้นจะถือว่าไม่ปลอดภัยสำหรับการเข้าถึงที่ไม่มีการแยกส่วนในขณะที่publicวิธีการนั้นถือว่าเป็นเกมฟรี (ส่วนใหญ่)

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


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

@ อีริค: หากคุณต้องการที่จะละทิ้งความปลอดภัยของวิธีการจนกว่าคุณจะพร้อมที่จะดี แต่ฉันหมายถึงวิธีการที่ถือว่าปลอดภัยซึ่งในกรณีที่ไม่มีการเปิดเผย
Guvante

10

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


ขอบคุณสำหรับคำตอบ. ฉันมักจะหัวชนฝายืนยันว่า "ทุกอย่าง" เรื่องเมื่อเขียนโค้ด :)
bopapa_1979

1
คุณเป็นเอริคที่ถูกต้องมันเป็นการแสดงความคิดเห็นทิ้งไปเล็กน้อย หวังว่าคำตอบที่เหลือของฉันก็มีประโยชน์ ฉันคิดว่าคำตอบของ Eric Lippert คือสิ่งที่ฉันพยายามแสดง แต่เขาอธิบายได้ดีขึ้นมาก
amondiamond ǤeezeƦ

8

ฉันสงสัยว่า "ง่ายกว่าที่จะทำให้เป็นสาธารณะในภายหลัง" ใช่ไหม.

กฎการกำหนดขอบเขตหมายความว่าวิธีการนั้นจะสามารถมองเห็นได้เท่านั้นinternalดังนั้นจึงไม่สำคัญว่าจะมีการทำเครื่องหมายpublicหรือinternalไม่

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


1
+1 สำหรับการตอกย้ำสถานการณ์ "คลาสสาธารณะกลายเป็นภายใน" ที่ชัดเจน
bopapa_1979

8

ในบางกรณีอาจเป็นได้ว่าประเภทภายในใช้ส่วนต่อประสานสาธารณะซึ่งหมายความว่าวิธีการใด ๆ ที่กำหนดไว้บนส่วนต่อประสานนั้นจะต้องประกาศเป็นสาธารณะ


2

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


2
มีคำถามที่เกี่ยวข้องอยู่แล้ว: stackoverflow.com/questions/711361/…
Mario Corchero

0

internalบอกว่าสมาชิกสามารถเข้าถึงได้จากภายในแอสเซมบลีเดียวกันเท่านั้น ชั้นเรียนอื่น ๆ ในการชุมนุมที่สามารถเข้าถึงinternal publicสมาชิก แต่จะไม่สามารถเข้าถึงprivateหรือprotectedสมาชิกinternalหรือไม่


แต่ถ้าวิธีการถูกทำเครื่องหมายinternalแทนpublicการมองเห็นของพวกเขาจะไม่เปลี่ยนแปลง ฉันเชื่อว่านั่นคือสิ่งที่ OP ถาม
Oded

1
@zapthedingbat - โพสต์ถูกต้องตามจริง แต่จริง ๆ แล้วมันตอบคำถามหรือไม่
Oded

1
ใช่คำถามคือทำไมพวกเขาทำอย่างนั้น ฉันเข้าใจพื้นฐานของขอบเขต ขอบคุณสำหรับความพยายาม แต่!
bopapa_1979

0

วันนี้ฉันต้องดิ้นรนต่อสู้ จนถึงตอนนี้ฉันจะได้กล่าวว่าวิธีการทั้งหมดควรจะถูกทำเครื่องหมายด้วยinternalถ้าชั้นเรียนเป็นinternalและจะได้พิจารณาสิ่งอื่นใดเพียงแค่การเขียนโค้ดที่ไม่ดีหรือความเกียจคร้านโดยเฉพาะอย่างยิ่งในการพัฒนาองค์กร; อย่างไรก็ตามฉันต้องชั้นเรียนย่อยpublicและแทนที่หนึ่งในวิธีการมัน:

internal class SslStreamEx : System.Net.Security.SslStream
{
    public override void Close()
    {
        try
        {
            // Send close_notify manually
        }
        finally
        {
            base.Close();
        }
    }
}

วิธีการจะต้องเป็นpublicและฉันคิดว่ามันไม่มีเหตุผลที่จะกำหนดวิธีการinternalยกเว้นว่าพวกเขาจะต้องเป็นจริงอย่างที่ Eric Lippert กล่าว

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


0

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


0

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

แบบนี้:

public sealed class MyCurrentlySealedClass
{
    protected void MyCurretlyPrivateMethod()
    {
    }
}

ตาม "รูปแบบ" ที่ฉันได้กล่าวมาข้างต้นนี้ควรจะดีอย่างสมบูรณ์ มันเป็นไปตามความคิดเดียวกัน มันทำงานเป็นprivateวิธีการเนื่องจากคุณไม่สามารถรับชั้นเรียน แต่ถ้าคุณลบsealedข้อ จำกัด มันก็ยังใช้ได้: คลาสที่สืบทอดมาสามารถดูวิธีนี้ได้ซึ่งเป็นสิ่งที่ฉันต้องการบรรลุ แต่คุณได้รับการเตือน: หรือCS0628 CA1047ทั้งสองอย่างเกี่ยวกับอย่าประกาศprotectedสมาชิกในsealedชั้นเรียน ยิ่งกว่านั้นฉันได้พบข้อตกลงอย่างสมบูรณ์เกี่ยวกับว่ามันหมดสติ: คำเตือน 'สมาชิกที่ได้รับการป้องกันในชั้นเรียนปิดผนึก' (ชั้นเดียว)

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


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