คุณช่วยอธิบายการใช้งานจริงสำหรับinternal
คำหลักใน C # ได้ไหม?
ฉันรู้ว่าinternal
ตัวดัดแปลง จำกัด การเข้าถึงแอสเซมบลีปัจจุบัน แต่เมื่อใดและในสถานการณ์ใดที่ฉันควรใช้
คุณช่วยอธิบายการใช้งานจริงสำหรับinternal
คำหลักใน C # ได้ไหม?
ฉันรู้ว่าinternal
ตัวดัดแปลง จำกัด การเข้าถึงแอสเซมบลีปัจจุบัน แต่เมื่อใดและในสถานการณ์ใดที่ฉันควรใช้
คำตอบ:
คลาส / วิธีการยูทิลิตี้หรือตัวช่วยที่คุณต้องการเข้าถึงจากคลาสอื่น ๆ ภายในแอสเซมบลีเดียวกัน แต่คุณต้องการให้แน่ใจว่าโค้ดในแอสเซมบลีอื่นไม่สามารถเข้าถึงได้
จากMSDN (ผ่าน archive.org):
การใช้งานทั่วไปของการเข้าถึงภายในคือการพัฒนาโดยใช้ส่วนประกอบเนื่องจากจะทำให้กลุ่มส่วนประกอบสามารถทำงานร่วมกันอย่างเป็นส่วนตัวโดยไม่ต้องสัมผัสกับส่วนที่เหลือของรหัสแอปพลิเคชัน ตัวอย่างเช่นกรอบงานสำหรับการสร้างอินเทอร์เฟซผู้ใช้แบบกราฟิกสามารถให้คลาส Control และ Form ที่ให้ความร่วมมือกับสมาชิกที่ใช้การเข้าถึงภายใน เนื่องจากสมาชิกเหล่านี้เป็นภายในจึงไม่ได้รับรหัสที่ใช้เฟรมเวิร์ก
คุณยังสามารถใช้ตัวแก้ไขภายในพร้อมกับInternalsVisibleTo
แอตทริบิวต์ระดับแอสเซมบลีเพื่อสร้างแอสเซมบลี "เพื่อน" ที่ได้รับการเข้าถึงพิเศษไปยังคลาสภายในแอสเซมบลีเป้าหมาย
สิ่งนี้มีประโยชน์สำหรับการสร้างชุดทดสอบหน่วยที่อนุญาตให้เรียกสมาชิกภายในของชุดประกอบที่จะทดสอบ แน่นอนว่าไม่มีแอสเซมบลีอื่น ๆ ที่ได้รับสิทธิ์การเข้าถึงระดับนี้ดังนั้นเมื่อคุณปล่อยระบบของคุณ
หาก Bob ต้องการ BigImportantClass จากนั้น Bob ต้องรับคนที่เป็นเจ้าของโครงการ A เพื่อลงทะเบียนเพื่อรับประกันว่า BigImportantClass จะถูกเขียนขึ้นเพื่อตอบสนองความต้องการของเขาทดสอบเพื่อให้แน่ใจว่าตรงกับความต้องการของเขาถูกจัดทำเป็นเอกสารตามความต้องการของเขาและกระบวนการ จะถูกวางไว้เพื่อให้แน่ใจว่ามันจะไม่เปลี่ยนแปลงเพื่อที่จะไม่ตอบสนองความต้องการของเขาอีกต่อไป
หากชั้นเรียนเป็นแบบภายในก็ไม่จำเป็นต้องผ่านกระบวนการดังกล่าวซึ่งจะช่วยประหยัดงบประมาณสำหรับโครงการ A ที่พวกเขาสามารถใช้ในสิ่งอื่นได้
จุดภายในไม่ได้ทำให้ชีวิตยากสำหรับบ๊อบ มันช่วยให้คุณสามารถควบคุมสิ่งที่สัญญาว่าจะให้โครงการ A ราคาแพงเกี่ยวกับคุณสมบัติอายุการใช้งานความเข้ากันได้และอื่น ๆ
อีกเหตุผลที่ควรใช้ภายในคือถ้าคุณทำให้สับสนไบนารีของคุณ ผู้ obfuscator รู้ว่ามันปลอดภัยที่จะแย่งชื่อคลาสของคลาสภายในใด ๆ ในขณะที่ชื่อของคลาสสาธารณะไม่สามารถถูกรบกวนได้เพราะมันอาจทำลายการอ้างอิงที่มีอยู่
หากคุณกำลังเขียน DLL ที่รวมเอาฟังก์ชันการทำงานที่ซับซ้อนจำนวนหนึ่งไว้ใน API สาธารณะอย่างง่ายระบบจะใช้“ ภายใน” กับสมาชิกคลาสซึ่งไม่ควรเปิดเผยต่อสาธารณะ
การซ่อนความซับซ้อน (aka encapsulation) เป็นแนวคิดหลักของวิศวกรรมซอฟต์แวร์ที่มีคุณภาพ
คำหลักภายในถูกใช้อย่างมากเมื่อคุณสร้าง wrapper ผ่านโค้ดที่ไม่มีการจัดการ
เมื่อคุณมีไลบรารี่ที่ใช้ C / C ++ ที่คุณต้องการ DllImport คุณสามารถนำเข้าฟังก์ชั่นเหล่านี้เป็นฟังก์ชั่นคงที่ของคลาสและทำให้พวกมันเป็นภายในดังนั้นผู้ใช้ของคุณจะสามารถเข้าถึง wrapper ของคุณและไม่ใช่ API ดั้งเดิม ยุ่งกับอะไร ฟังก์ชั่นคงที่คุณสามารถใช้มันได้ทุกที่ในชุดประกอบสำหรับคลาส wrapper หลายคลาสที่คุณต้องการ
คุณสามารถดู Mono.Cairo มันเป็นเสื้อคลุมรอบ ๆ cairo ไลบรารี่ที่ใช้วิธีนี้
ถูกขับเคลื่อนโดย "ใช้เป็นตัวดัดแปลงที่เข้มงวดที่สุดเท่าที่จะทำได้" กฎที่ฉันใช้ภายในทุกที่ที่ฉันต้องการเข้าถึงพูดวิธีการจากคลาสอื่นจนกว่าฉันจะต้องเข้าถึงมันอย่างชัดเจนจากชุดประกอบอื่น
เนื่องจากอินเทอร์เฟซแอสเซมบลีมักแคบกว่าผลรวมของอินเทอร์เฟซของคลาสมีหลาย ๆ ที่ที่ฉันใช้
ฉันพบว่าภายในนั้นมีการใช้มากเกินไป คุณไม่ควรเปิดเผยฟังก์ชั่นบางอย่างต่อคลาสที่คุณไม่ต้องการให้กับผู้บริโภครายอื่น
ในความคิดของฉันแบ่งอินเทอร์เฟซการแบ่งสิ่งที่เป็นนามธรรม นี่ไม่ได้เป็นการบอกว่าไม่ควรใช้ แต่วิธีที่ดีกว่าคือการปรับโครงสร้างให้เป็นคลาสที่แตกต่างหรือใช้ในวิธีที่ต่างกันถ้าเป็นไปได้ อย่างไรก็ตามสิ่งนี้อาจไม่สามารถทำได้เสมอไป
สาเหตุที่ทำให้เกิดปัญหาคือผู้พัฒนารายอื่นอาจถูกเรียกเก็บเงินกับการสร้างคลาสอื่นในแอสเซมบลีเดียวกับที่คุณเป็น การมีผู้ฝึกงานภายในช่วยลดความชัดเจนของสิ่งที่เป็นนามธรรมและอาจทำให้เกิดปัญหาหากใช้งานในทางที่ผิด มันจะเป็นปัญหาเดียวกับที่คุณเปิดเผยต่อสาธารณะ คลาสอื่นที่ถูกสร้างโดยผู้พัฒนารายอื่นยังคงเป็นผู้บริโภคเหมือนกับคลาสภายนอก คลาส Abstraction และ Encapsulation ไม่ได้มีไว้สำหรับการป้องกัน / จากคลาสภายนอก แต่สำหรับคลาสใด ๆ และทุกคลาส
ปัญหาอีกประการหนึ่งคือนักพัฒนาจำนวนมากจะคิดว่าพวกเขาอาจจำเป็นต้องใช้มันในที่อื่น ๆ ในการชุมนุมและทำเครื่องหมายว่าเป็นอย่างไรก็ตามภายในแม้ว่าพวกเขาไม่ต้องการในเวลานั้น นักพัฒนารายอื่นอาจคิดว่ามันอยู่ที่นั่นเพื่อการถ่าย โดยทั่วไปคุณต้องการทำเครื่องหมายว่าเป็นส่วนตัวจนกว่าคุณจะมีความต้องการที่ชัดเจน
แต่สิ่งเหล่านี้อาจเป็นเรื่องส่วนตัวและฉันไม่ได้บอกว่าไม่ควรใช้ เพียงใช้เมื่อจำเป็น
เห็นสิ่งที่น่าสนใจในวันอื่น ๆ อาจเป็นสัปดาห์ในบล็อกที่ฉันจำไม่ได้ โดยทั่วไปฉันไม่สามารถรับเครดิตได้ แต่ฉันคิดว่าอาจมีแอปพลิเคชันที่มีประโยชน์
สมมติว่าคุณต้องการให้คลาสนามธรรมมองเห็นโดยการชุมนุมอื่น แต่คุณไม่ต้องการให้ใครบางคนได้รับมรดกจากมัน ปิดผนึกจะไม่ทำงานเพราะเป็นนามธรรมด้วยเหตุผลชั้นเรียนอื่นในการชุมนุมนั้นสืบทอดมาจากมัน ส่วนบุคคลจะไม่ทำงานเพราะคุณอาจต้องการประกาศคลาส Parent บางแห่งในชุดประกอบอื่น
namespace Base.Assembly { ชั้นนามธรรมสาธารณะ { โมฆะนามธรรมภายในเป็นโมฆะ SomeMethod (); } // มันใช้งานได้ดีเพราะมันอยู่ในชุดประกอบเดียวกัน ChildWithin คลาสพับลิก: พาเรนต์ { การแทนที่ภายในเป็นโมฆะ SomeMethod () { } } } namespace Another.Assembly { // Kaboom เพราะคุณไม่สามารถแทนที่วิธีการภายใน คลาส ChildOutside สาธารณะ: ผู้ปกครอง { } การทดสอบระดับสาธารณะ { // สบายดี ผู้ปกครองส่วนตัว _parent; การทดสอบสาธารณะ () { // ยังไม่เป็นไร _parent = new ChildWithin (); } } }
อย่างที่คุณเห็นมันช่วยให้บางคนสามารถใช้คลาส Parent ได้อย่างมีประสิทธิภาพโดยไม่สามารถสืบทอดได้
ตัวอย่างนี้มีสองไฟล์: Assembly1.cs และ Assembly2.cs ไฟล์แรกมีคลาสฐานภายในคือ BaseClass ในไฟล์ที่สองความพยายามในการสร้างอินสแตนซ์ BaseClass จะทำให้เกิดข้อผิดพลาด
// Assembly1.cs
// compile with: /target:library
internal class BaseClass
{
public static int intM = 0;
}
// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // CS0122
}
}
ในตัวอย่างนี้ใช้ไฟล์เดียวกับที่คุณใช้ในตัวอย่างที่ 1 และเปลี่ยนระดับการเข้าถึงของ BaseClass เพื่อประชาชน นอกจากนี้ยังเปลี่ยนระดับการเข้าถึงของสมาชิก IntM ไปภายใน ในกรณีนี้คุณสามารถยกตัวอย่างคลาสได้ แต่คุณไม่สามารถเข้าถึงสมาชิกภายในได้
// Assembly2.cs
// compile with: /target:library
public class BaseClass
{
internal static int intM = 0;
}
// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // Ok.
BaseClass.intM = 444; // CS0117
}
}
แหล่งที่มา : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx
เมื่อคุณมีวิธีการเรียน ฯลฯ ซึ่งจะต้องสามารถเข้าถึงได้ภายในขอบเขตของการชุมนุมในปัจจุบันและไม่เคยออกไปข้างนอก
ตัวอย่างเช่น DAL อาจมี ORM แต่วัตถุไม่ควรสัมผัสกับเลเยอร์ธุรกิจการโต้ตอบทั้งหมดควรทำผ่านวิธีการคงที่และส่งผ่านพารามิเตอร์ที่จำเป็น
การใช้งานภายในที่น่าสนใจมาก - โดยที่สมาชิกภายในของหลักสูตรจะถูก จำกัด เฉพาะในแอสเซมบลีที่ได้รับการประกาศเท่านั้น - กำลังรับฟังก์ชั่น "เพื่อน" ในระดับหนึ่ง สมาชิกเพื่อนเป็นสิ่งที่มองเห็นได้เฉพาะกับแอสเซมบลีอื่นบางอย่างภายนอกแอสเซมบลีที่ประกาศ C # ไม่มีการสนับสนุนสำหรับเพื่อน แต่ CLR ทำ
คุณสามารถใช้InternalsVisibleToAttributeเพื่อประกาศการชุมนุมเพื่อนและการอ้างอิงทั้งหมดจากภายในการชุมนุมเพื่อนจะปฏิบัติต่อสมาชิกภายในของการประกาศการชุมนุมของคุณเป็นสาธารณะภายในขอบเขตของการชุมนุมเพื่อน ปัญหานี้คือสมาชิกภายในทั้งหมดมองเห็นได้ คุณไม่สามารถเลือกและเลือก
การใช้งานที่ดีสำหรับ InternalsVisibleTo คือการเปิดเผยสมาชิกภายในหลาย ๆ กลุ่มให้เข้าร่วมการทดสอบหน่วยดังนั้นจึงไม่จำเป็นต้องใช้การสะท้อนกลับที่ซับซ้อนเพื่อทดสอบสมาชิกเหล่านั้น สมาชิกภายในทั้งหมดที่มองเห็นไม่ได้เป็นปัญหามากนักอย่างไรก็ตามการใช้วิธีการนี้จะทำให้อินเทอร์เฟซของคลาสของคุณยุ่งมากและอาจทำลาย encapsulation ภายในแอสเซมบลีที่ประกาศ
กฎของหัวแม่มือมีสมาชิกสองประเภท:
การลดเสียงรบกวนยิ่งคุณใช้ห้องสมุดน้อยลง การพิสูจน์อักษรการงัดแงะ / ความปลอดภัยเป็นอีกหนึ่ง (แม้ว่า Reflection สามารถเอาชนะได้)
คลาสภายในช่วยให้คุณสามารถ จำกัด API ของแอสเซมบลีของคุณ สิ่งนี้มีประโยชน์เช่นทำให้ API ของคุณเข้าใจง่ายขึ้น
นอกจากนี้หากมีข้อผิดพลาดในการประกอบของคุณมีโอกาสน้อยที่การแก้ไขที่แนะนำการเปลี่ยนแปลงที่ทำลาย หากไม่มีคลาสภายในคุณจะต้องสมมติว่าการเปลี่ยนสมาชิกสาธารณะของคลาสใด ๆ จะเป็นการเปลี่ยนแปลงที่ไม่สิ้นสุด ด้วยคลาสภายในคุณสามารถสันนิษฐานได้ว่าการแก้ไขสมาชิกสาธารณะจะแบ่งเฉพาะ API ภายในของแอสเซมบลีเท่านั้น (และแอสเซมบลีใด ๆ ที่อ้างอิงในแอตทริบิวต์ InternalsVisibleTo)
ฉันชอบการห่อหุ้มที่ระดับชั้นเรียนและในระดับการชุมนุม มีบางคนที่ไม่เห็นด้วยกับสิ่งนี้ แต่ก็ยินดีที่ได้ทราบว่ามีฟังก์ชั่นให้ใช้งาน
ฉันมีโครงการที่ใช้ LINQ-to-SQL สำหรับ data back-end ฉันมีเนมสเปซหลักสองรายการ: Biz และ Data รูปแบบข้อมูล LINQ อาศัยอยู่ในข้อมูลและมีการทำเครื่องหมาย "ภายใน"; Biz namespace มีคลาสสาธารณะซึ่งล้อมรอบคลาสข้อมูล LINQ
ดังนั้นมีData.Client
และBiz.Client
; หลังเปิดเผยคุณสมบัติที่เกี่ยวข้องทั้งหมดของวัตถุข้อมูลเช่น:
private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }
วัตถุ Biz มีตัวสร้างส่วนตัว (เพื่อบังคับให้ใช้วิธีการจากโรงงาน) และตัวสร้างภายในซึ่งมีลักษณะดังนี้:
internal Client(Data.Client client) {
this._client = client;
}
สามารถใช้คลาสธุรกิจใดก็ได้ในไลบรารี แต่ front-end (UI) ไม่มีวิธีเข้าถึงโมเดลข้อมูลโดยตรงเพื่อให้แน่ใจว่าเลเยอร์ธุรกิจทำหน้าที่เป็นตัวกลางเสมอ
นี่เป็นครั้งแรกที่ฉันใช้internal
มันมากและพิสูจน์ได้ว่ามีประโยชน์มาก
มีหลายกรณีที่เหมาะสมที่จะทำให้สมาชิกของคลาสinternal
เป็น ตัวอย่างหนึ่งอาจเป็นได้หากคุณต้องการควบคุมวิธีการสร้างอินสแตนซ์ของคลาส สมมติว่าคุณมีโรงงานบางอย่างสำหรับสร้างอินสแตนซ์ของคลาส คุณสามารถสร้างคอนสตรัคinternal
เพื่อให้โรงงาน (ที่อยู่ในแอสเซมบลีเดียวกัน) สามารถสร้างอินสแตนซ์ของคลาสได้ แต่โค้ดภายนอกแอสเซมบลีนั้นไม่สามารถทำได้
อย่างไรก็ตามฉันไม่สามารถมองเห็นจุดใด ๆ ในการสร้างชั้นเรียนหรือสมาชิกinternal
โดยไม่มีเหตุผลเฉพาะเพียงเล็กน้อยเท่าที่เหมาะสมที่จะทำให้พวกเขาpublic
หรือprivate
ไม่มีเหตุผลที่เฉพาะเจาะจง
สิ่งเดียวที่ฉันเคยใช้คือคีย์เวิร์ดภายในคือรหัสตรวจสอบใบอนุญาตในผลิตภัณฑ์ของฉัน ;-)
การใช้คำหลักภายในหนึ่งครั้งคือ จำกัด การเข้าถึงการใช้งานที่เป็นรูปธรรมจากผู้ใช้ชุดประกอบของคุณ
หากคุณมีโรงงานหรือที่ตั้งศูนย์กลางอื่น ๆ สำหรับการสร้างวัตถุผู้ใช้งานแอสเซมบลีของคุณต้องจัดการกับอินเทอร์เฟซสาธารณะหรือคลาสพื้นฐานที่เป็นนามธรรมเท่านั้น
นอกจากนี้ตัวสร้างภายในช่วยให้คุณสามารถควบคุมสถานที่และเวลาที่คลาสสาธารณะเป็นอย่างอื่น
วิธีการเกี่ยวกับเรื่องนี้: โดยทั่วไปจะแนะนำให้คุณไม่เปิดเผยรายชื่อวัตถุให้กับผู้ใช้ภายนอกของการชุมนุมค่อนข้างเปิดเผย IEnumerable แต่มันง่ายกว่ามากในการใช้ List List ภายในแอสเซมบลีเนื่องจากคุณได้รับไวยากรณ์ของอาเรย์และเมธอด List อื่น ๆ ทั้งหมด ดังนั้นฉันมักจะมีคุณสมบัติภายในเปิดเผยรายชื่อที่จะใช้ภายในการชุมนุม
ยินดีต้อนรับความคิดเห็นเกี่ยวกับวิธีการนี้
โปรดทราบว่าคลาสใด ๆ ที่กำหนดไว้public
จะปรากฏขึ้นโดยอัตโนมัติใน Intellisense เมื่อมีคนดูเนมสเปซโครงการของคุณ จากมุมมอง API เป็นสิ่งสำคัญที่จะแสดงเฉพาะผู้ใช้ในโครงการของคุณในชั้นเรียนที่สามารถใช้ ใช้internal
คำหลักเพื่อซ่อนสิ่งที่ไม่ควรเห็น
หากคุณBig_Important_Class
สำหรับโครงการ A มีไว้สำหรับใช้นอกโครงการของคุณคุณไม่ควรทำเครื่องหมายinternal
โครงการมีวัตถุประสงค์สำหรับใช้ภายนอกโครงการของคุณแล้วคุณไม่ควรทำเครื่องหมาย
อย่างไรก็ตามในหลายโครงการคุณมักจะมีคลาสที่มีไว้สำหรับใช้ภายในโครงการเท่านั้น ตัวอย่างเช่นคุณอาจมีคลาสที่เก็บอาร์กิวเมนต์สำหรับการร้องขอเธรดที่กำหนดพารามิเตอร์ ในกรณีเหล่านี้คุณควรทำเครื่องหมายinternal
ว่าไม่มีเหตุผลอื่นนอกจากปกป้องตนเองจากการเปลี่ยน API โดยไม่ตั้งใจ
private
คำหลักเท่านั้นที่สามารถนำมาใช้ในการเรียนหรือ structs ที่ได้กำหนดไว้ในระดับอื่นหรือ struct คลาสที่กำหนดภายในเนมสเปซสามารถประกาศpublic
หรือinternal
เท่านั้น
แนวคิดคือเมื่อคุณออกแบบไลบรารีเฉพาะคลาสที่มีวัตถุประสงค์เพื่อใช้จากภายนอก (โดยไคลเอนต์ของไลบรารีของคุณ) ควรเป็นสาธารณะ วิธีนี้คุณสามารถซ่อนคลาสได้
เป็นต้น
หากคุณกำลังพัฒนาโซลูชันภายในองค์กรมากกว่าการใช้องค์ประกอบภายในไม่ใช่สิ่งสำคัญที่ฉันเดาเพราะโดยปกติแล้วลูกค้าจะมีการติดต่อกับคุณและ / หรือการเข้าถึงรหัสอย่างต่อเนื่อง สิ่งเหล่านี้ค่อนข้างสำคัญสำหรับนักพัฒนาห้องสมุด
เมื่อคุณมีคลาสหรือวิธีการที่ไม่พอดีกับกระบวนทัศน์เชิงวัตถุซึ่งทำสิ่งอันตรายซึ่งต้องถูกเรียกจากคลาสและวิธีการอื่นภายใต้การควบคุมของคุณและคุณไม่ต้องการให้ใครใช้ .
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}