ฉันมีคลาส Foo
และคลาสคงที่ FooFactory
ซึ่งใช้เพื่อสร้างอินสแตนซ์ของคลาสที่ได้รับ Foo
และ Foo
ผ่าน API ต่อไปนี้:
public static class FooFactory {
public static T Create<T>() where T : Foo, new() {
...
return new T();
}
}
ความเชี่ยวชาญพิเศษ new()
ของพารามิเตอร์ประเภท T
กำหนดให้ Foo
มีตัวสร้างเริ่มต้น public
กล่าวอีกนัยหนึ่ง ไม่มีอะไรขัดขวางผู้ใช้ Foo
ในการเริ่มต้นคลาสโดยตรงผ่าน Constructor
อย่างไรก็ตาม
FooFactory
มีวัตถุประสงค์เพื่อติดตามอินสแตนซ์Foo
ทั้งหมด ดังนั้นฉันต้องการบังคับให้ผู้ใช้สร้างอินสแตนซ์ที่ได้รับFoo
และFoo
ทั้งหมดผ่านทางFooFactory
สิ่งที่ใกล้เคียงที่สุดที่ฉันสามารถป้องกันการเริ่มต้นคอนสตรัคเตอร์โดยตรงได้คือการตกแต่งคอนสตรัคเตอร์เริ่มต้น Foo
ด้วยแอตทริบิวต์ [Obsolete("message", error: true)]
:
public class Foo {
[Obsolete("Fail", true)]
public Foo() { ... }
}
ด้วยการตกแต่งนี้ โค้ดจะไม่คอมไพล์เมื่อฉันเรียกใช้คอนสตรัคเตอร์เริ่มต้น Foo
โดยตรง ในขณะที่การเริ่มต้นผ่าน FooFactory.Create<Foo>()
ใช้งานได้
แต่ด้วยการตกแต่ง [Obsolete]
นี้ ฉันยังคงมีปัญหากับคลาสที่ได้รับ สิ่งต่อไปนี้จะไม่คอมไพล์ด้วยซ้ำเนื่องจากการตั้งค่า error: true
สำหรับตัวสร้างเริ่มต้น Foo
:
public class Bar : Foo { ... }
public class Baz : Foo { public Baz() : base() { ... } }
ฉันสามารถประกาศโอเวอร์โหลดคอนสตรัคเตอร์ protected
Foo
ที่คลาสที่ได้รับเรียกใช้แทนคอนสตรัคเตอร์เริ่มต้น แต่จากนั้นฉันจะไม่สามารถป้องกันการเริ่มต้นโดยตรงของคลาสที่ได้รับโดยทางโปรแกรมได้
ถ้าฉันตั้งค่า error
ใน ObsoleteAttribute
เป็น false
ฉันจะได้รับคำเตือนการคอมไพล์แทน แต่ฉันอยากจะมีความท้อแท้มากกว่าคำเตือน...
มีวิธีใดบ้างที่ฉันสามารถป้องกันการเรียกใช้คอนสตรัคเตอร์เริ่มต้นโดยตรงโดยทางโปรแกรมสำหรับทั้ง Foo
และคลาสที่ได้รับเมื่อจำเป็นต้องประกาศคอนสตรัคเตอร์เริ่มต้น public
?
internal
ไปที่ไลบรารีและลบข้อจำกัดnew()
ซึ่งหมายความว่าคุณต้องรับผิดชอบในการสร้าง Foo ทุกประเภท - person Nkosi   schedule 04.09.2017new()
จะใช้ได้ก็ต่อเมื่อมีการประกาศตัวสร้างpublic
- person Anders Gustafsson   schedule 04.09.2017Foo
ทำการติดตามอินสแตนซ์ ผ่านการเรียกใช้ฟิลด์คงที่และthis.GetType()
- person Jeroen Mostert   schedule 04.09.2017Baz
ที่สามารถสร้างได้โดยตรง ผู้เขียนสามารถเสมอจัดเตรียมตัวสร้างสาธารณะสำหรับผู้ใช้ที่ข้ามความต้องการใดๆ ของคุณ ไม่ว่าFoo
จะถูกตั้งค่าอย่างไรก็ตาม หากคุณถือว่าผู้เขียนสมรู้ร่วมคิดอยู่เสมอและสามารถสันนิษฐานได้ว่าพวกเขาจะประพฤติตัวได้ คำแนะนำที่สองของฉันคือละทิ้งข้อจำกัดnew()
และส่งต่อการเรียกคอนสตรัคเตอร์เป็นFunc<T>
ไปยังเมธอดภายใน วิธีนี้จะรักษาความปลอดภัยของประเภทแต่จะยุ่งยากสำหรับผู้เขียน - person Jeroen Mostert   schedule 04.09.2017new()
ก็คือสามารถพกพาข้ามแพลตฟอร์มได้ การสะท้อนโดยเฉพาะอย่างยิ่งกับวิธีที่ไม่ใช่public
อาจเล่นได้ไม่ดีในทุกแพลตฟอร์มเสมอไป - person Anders Gustafsson   schedule 04.09.2017Foo
ได้หรือไม่ หากนั่นไม่ใช่ข้อกำหนด การแก้ปัญหาของคุณก็จะง่ายกว่ามาก - person Eric Lippert   schedule 04.09.2017