การล่วงละเมิดทั่วไปคืออะไร?


35

ในขณะที่ตรวจสอบรหัสบางอย่างฉันสังเกตเห็นโอกาสที่จะเปลี่ยนเป็นใช้ชื่อสามัญ โค้ด (obfuscated) ดูเหมือนว่า:

public void DoAllTheThings(Type typeOfTarget, object[] possibleTargets)
{
    var someProperty = typeOfTarget.GetProperty(possibleTargets[0]);
    ...
}

รหัสนี้อาจถูกแทนที่ด้วย generics เช่น:

public void DoAllTheThings<T>(object[] possibleTargets[0])
{
    var someProperty = type(T).getProperty(possibleTargets[0]);
    ...
}

ในการวิจัยถึงประโยชน์และข้อบกพร่องของวิธีการนี้ผมพบว่าในระยะที่เรียกว่าการทำผิดกฎทั่วไป ดู:

คำถามของฉันมีสองส่วน:

  1. จะมีประโยชน์ใด ๆ ในการย้ายไปยาสามัญเช่นนี้? (ประสิทธิภาพอ่านง่าย)
  2. การละเมิดทั่วไปคืออะไร และใช้งานทั่วไปทุกครั้งที่มีพารามิเตอร์ประเภทเป็นการละเมิด ?

2
ฉันสับสน คุณรู้จักชื่อของคุณสมบัติล่วงหน้า แต่ไม่ใช่ชนิดของวัตถุดังนั้นคุณจึงใช้การสะท้อนเพื่อรับค่า
MetaFight

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

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

2
ตัวอย่างของคุณแปลกกว่าเดิม แต่ฉันรู้สึกว่ามันเป็นสิ่งที่ห้องสมุด IoD ทำอย่างมาก
Ewan

14
ไม่ใช่คำตอบ แต่ควรสังเกตว่าตัวอย่างที่สองยืดหยุ่นน้อยกว่าตัวอย่างแรก ทำให้ฟังก์ชั่นทั่วไปลบความสามารถของคุณที่จะผ่านในประเภท runtime; ยาสามัญจำเป็นต้องรู้ชนิดในเวลารวบรวม
KChaloux

คำตอบ:


87

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

ในตัวอย่างของคุณฉันคาดหวังว่าการใช้ยาชื่อสามัญที่เหมาะสมเพื่อเปลี่ยนobject[]เป็นT[]หรือคล้ายกันและหลีกเลี่ยงTypeหรือtypeทั้งหมด ซึ่งอาจต้องมีการเปลี่ยนโครงสร้างใหม่อย่างมีนัยสำคัญที่อื่น แต่หากใช้ยาชื่อสามัญที่เหมาะสมในกรณีนี้ควรสรุปโดยรวมได้ง่ายขึ้นเมื่อคุณทำเสร็จแล้ว


3
นั่นเป็นจุดที่ดี ฉันจะยอมรับโค้ดที่ทำให้ฉันคิดว่านี่เป็นการแสดงที่อ่านยาก ฉันจะดูว่าฉันจะเปลี่ยนไปobject[] T[]
SyntaxRules

3
ฉันชอบคุณลักษณะการลบและจัดเรียงใหม่
copper

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

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

4
@MichaelAnderson "ลบรหัสแทนเพียงแค่จัดเรียงใหม่" รวมถึงการลดการทำซ้ำ
Caleth

5

ผมจะใช้กฎขรึม: Generics เหมือนการเขียนโปรแกรมโครงสร้างอื่น ๆที่มีอยู่ในการแก้ปัญหา หากไม่มีปัญหาในการแก้ปัญหาเรื่องยาชื่อสามัญการใช้สิ่งเหล่านี้เป็นการละเมิด

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

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


ดูเหมือนว่ามันจะดูอวดความกระตือรือร้นเกินไปดังนั้นให้ฉันเตือนคุณ:
ไม่มีกฎโดยไม่มีข้อยกเว้นในการเขียนโปรแกรม กฎนี้รวม


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

@ Christophe แน่นอนว่าเป็นคำถามที่ยุ่งยาก ใช่มีบางสถานการณ์ที่ควรละเว้นกฎของฉัน (ไม่มีกฎโดยไม่มีข้อยกเว้นในการเขียนโปรแกรม!) อย่างไรก็ตามกรณีการแปลงสตริงที่เฉพาะเจาะจงมีส่วนเกี่ยวข้องค่อนข้างจริง: 1. คุณต้องจัดการกับประเภทจำนวนเต็มลงนามและไม่ได้ลงนาม 2. คุณต้องปฏิบัติต่อการแปลงแบบลอยที่แยกจากการแปลงจำนวนเต็ม 3. คุณสามารถนำการใช้งานมาใช้ใหม่สำหรับประเภทจำนวนเต็มที่มีขนาดใหญ่ที่สุดสำหรับประเภทที่เล็กกว่าได้ คุณอาจต้องการเขียนเสื้อคลุมทั่วไปอย่างกระตือรือร้นเพื่อทำการคัดเลือกนักแสดง แต่แกนกลางนั้นจะไม่มีนายพล
cmaster

1
ฉันจะต่อต้านการเขียนแบบโปร (ใช้ล่วงหน้าแทนที่จะใช้ซ้ำ) ตามที่คาดว่าน่าจะเป็น YAGNI เมื่อคุณต้องการมันแล้ว refactor
Neil_UK

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

0

รหัสดูแปลก ๆ แต่ถ้าเราเรียกมันมันดูดีกว่าที่จะระบุประเภทเป็นแบบทั่วไป

https://stackoverflow.com/questions/10955579/passing-just-a-type-as-a-parameter-in-c-sharp

โดยส่วนตัวแล้วฉันไม่ชอบสิ่งเหล่านี้ที่ทำให้รหัสการโทรดูสวย เช่นสิ่ง 'คล่องแคล่ว' ทั้งหมดและวิธีการขยาย

แต่คุณต้องยอมรับว่ามันได้รับความนิยมดังต่อไปนี้ แม้แต่ไมโครซอฟท์ก็ใช้มันด้วยความสามัคคีตัวอย่างเช่น

container.RegisterType<IInterface,Concrete>()

0

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

คุณคิดว่าจะเปลี่ยนobject[]พารามิเตอร์Func<T,object>[]เป็นขั้นตอนถัดไปหรือไม่?

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