ข้อบกพร่องในการออกแบบที่ใหญ่ที่สุดใน C # หรือ. NET Framework โดยทั่วไปมีอะไรบ้าง
ตัวอย่าง: ไม่มีประเภทสตริงที่ไม่เป็นโมฆะและคุณต้องตรวจสอบ DBNull เมื่อดึงค่าจาก IDataReader
ข้อบกพร่องในการออกแบบที่ใหญ่ที่สุดใน C # หรือ. NET Framework โดยทั่วไปมีอะไรบ้าง
ตัวอย่าง: ไม่มีประเภทสตริงที่ไม่เป็นโมฆะและคุณต้องตรวจสอบ DBNull เมื่อดึงค่าจาก IDataReader
คำตอบ:
ฉันเห็นด้วยอย่างชัดเจนกับโพสต์นี้ (สำหรับผู้ที่ poo-poo ที่ไม่มี ToString มีแอตทริบิวต์ดีบักเกอร์เพื่อจัดเตรียมรูปแบบที่กำหนดเองสำหรับชั้นเรียนของคุณ)
ด้านบนของรายการด้านบนฉันจะเพิ่มคำขอที่สมเหตุสมผลดังต่อไปนี้:
T : new(string)หรือที่ไหนT : new(string, int)var e = new Foo(); e { Bar = baz };Either<T>" นั้นไม่ได้ดังนั้นฉันจึงชอบวิธีการประกาศประเภทพีชคณิตแบบปิดและบังคับใช้การจับคู่รูปแบบที่ละเอียดถี่ถ้วน (โดยทั่วไปแล้วการสนับสนุนชั้นหนึ่งสำหรับรูปแบบผู้เยี่ยมชม แต่ มีประสิทธิภาพมากขึ้น); ดังนั้นเพียงแค่ใช้ enums ขยายด้วยการสนับสนุนการจับคู่รูปแบบที่ละเอียดถี่ถ้วนและไม่อนุญาตกรณีที่ไม่ถูกต้องSystem.IOชั้นเรียนStreamได้รับการออกแบบมาไม่ดี อินเทอร์เฟซใด ๆ ที่ต้องใช้งานบางอย่างเพื่อโยนNotSupportedExceptionเป็นการออกแบบที่ไม่ดีIListควรจะง่ายกว่าที่เป็นอยู่มาก ในความเป็นจริงนี้อาจเป็นจริงหลายคอลเลกชันของอินเตอร์เฟซที่เป็นรูปธรรมเช่นICollection,INotifyPropertyChangedที่ใช้ชื่อฟิลด์เป็นสตริง คุณสามารถทำได้โดยใช้วิธีการขยายที่ใช้แลมบ์ดาด้วย a MemberExpressionเช่น () => Fooแต่ก็ไม่ค่อยมีประสิทธิภาพ
nameof()ดำเนินการสำหรับชื่อสมาชิกเดี่ยว แต่ใช้ไม่ได้ในชื่อทั่วไป ( nameof(T) == "T"แทนที่จะเป็นชื่ออาร์กิวเมนต์ประเภทจริง: คุณยังต้องทำtypeof(T).Name)) - และไม่อนุญาตให้คุณรับสตริง "เส้นทาง" เช่นnameof(this.ComplexProperty.Value) == "Value"การ จำกัด การใช้งานที่เป็นไปได้IArithmetic; อินเทอร์เฟซตัวดำเนินการที่ใช้ร่วมกันอื่น ๆ ที่เป็นประโยชน์สามารถทำได้เช่นกันreadonlyคีย์เวิร์ดและ C # 6.0 เพิ่มคุณสมบัติอัตโนมัติแบบอ่านอย่างเดียวแม้ว่าจะไม่เข้มงวดเท่ากับการรองรับภาษาที่แท้จริงสำหรับประเภทและค่าที่ไม่เปลี่ยนรูปก็เพียงพอแล้วสำหรับตอนนี้ฉันคิดว่า สิ่งเหล่านี้คือความระคายเคืองทั้งหมดที่ฉันเคยพบในสัปดาห์ที่ผ่านมา ฉันอาจจะใช้เวลาหลายชั่วโมงถ้าฉันตั้งใจกับมันจริงๆ C # 4.0 กำลังเพิ่มอาร์กิวเมนต์ที่มีชื่อเป็นทางเลือกและค่าเริ่มต้นซึ่งฉันเห็นด้วยอย่างชัดเจน
ตอนนี้สำหรับคำขอที่ไม่มีเหตุผล:
ได้โปรด? :-)
List<T>หนึ่งล้าน Ts คุณเสนอให้ถ่ายภาพรวมอย่างมีประสิทธิภาพได้อย่างไร # 21: ใช้readonlyคำหลัก .... ในขณะที่มีคำแนะนำที่ดีอยู่ที่นี่ แต่ส่วนใหญ่เป็นเพียงคำแนะนำไม่ใช่ข้อบกพร่องในการออกแบบ
Reset()วิธีการในการIEnumerator<T>เป็นความผิดพลาด (บล็อก iterator ข้อมูลจำเพาะภาษาแม้กระทั่งเรียกร้องที่ว่านี้พ่นยกเว้น)IEnumerable<out T>และFunc<in T, out TResult>แต่ไม่ใช่ประเภทคอนกรีต (เช่นList<T>)ApplicationException ค่อนข้างไม่ชอบ - นั่นเป็นความผิดพลาดหรือไม่?Containsจากนั้นAdd) ดังนั้นคอลเล็กชันที่ซิงโครไนซ์การดำเนินการที่แตกต่างกันจึงไม่เป็นประโยชน์ทั้งหมด
System.Collections.ConcurrentTryAddGetOrAddTryRemoveusing/ lockรูปแบบได้มากขึ้น - บางทีอนุญาตให้แชร์ไวยากรณ์ที่ใช้ซ้ำได้ (ขยายได้?) คุณสามารถจำลองสิ่งนี้ได้โดยการย้อนกลับIDisposableและใช้usingงาน แต่อาจชัดเจนกว่านี้Foo(SqlConnection! connection)(ที่ฉีด null-check / throw) จะดี (ตรงกันข้ามกับint?ฯลฯ )
dynamicหรือคุณสามารถเปิดใช้งานได้เช่นนี้foreachขยายซึ่งหมายความว่า anon-method / lambdas จับตัวแปรเดียวแทนที่จะเป็นตัวแปรเดียวต่อการวนซ้ำ (เจ็บปวดกับ threading / async / etc)
ApplicationExceptionเป็นความผิดพลาด - ไม่มีประโยชน์อย่างที่พวกเขาหวัง พวกเขายังกล่าวว่าควรจะได้รับSystem.Exception abstract
TextWriter เป็นคลาสพื้นฐานของ StreamWriter wtf?
นั่นทำให้ฉันสับสนอย่างมาก
ตัวสร้าง C # pet ขนาดเล็ก - ตัวสร้างใช้ไวยากรณ์ C ++ / Java เพื่อให้ตัวสร้างเป็นชื่อเดียวกับคลาส
New()หรือctor()จะดีกว่านี้มาก
และแน่นอนว่าเครื่องมือเช่น coderush ทำให้ปัญหานี้น้อยลงสำหรับการเปลี่ยนชื่อคลาส แต่จาก POV ที่อ่านง่าย New () ให้ความชัดเจนมาก
class Foo { new(int j) {i = j} int i; }
Newคำหลักที่เป็นตัวพิมพ์ใหญ่ขัดต่อแบบแผน) แต่ฉันลังเลที่จะเรียกมันว่าข้อบกพร่องในการออกแบบ พวกเขาต้องการดึงดูดนักพัฒนา C ++ / Java ที่มีอยู่และการยืมรูปแบบวากยสัมพันธ์เก่า ๆ ที่โง่เขลาจำนวนมากช่วยให้พวกเขาบรรลุเป้าหมายได้
ฉันไม่เข้าใจว่าคุณทำไม่ได้
โดยที่ T: ใหม่ (U)
ดังนั้นคุณจึงประกาศว่าประเภททั่วไป T มีตัวสร้างที่ไม่ใช่ค่าเริ่มต้น
แก้ไข:
ฉันต้องการทำสิ่งนี้:
public class A
{
public A(string text)
{
}
}
public class Gen<T> where T : new(string text)
{
}
ฉันแปลกใจจริงๆที่ฉันเป็นคนแรกที่พูดถึงเรื่องนี้:
ADO.NET ชุดข้อมูลที่พิมพ์ไม่แสดงคอลัมน์ที่เป็นโมฆะเป็นคุณสมบัติของประเภทที่ว่างเปล่า คุณควรจะเขียนสิ่งนี้ได้:
int? i = myRec.Field;
myRec.Field = null;
แต่คุณต้องเขียนสิ่งนี้ซึ่งมันโง่มาก:
int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field;
myRec.SetFieldNull();
นี่เป็นสิ่งที่น่ารำคาญใน. NET 2.0 และตอนนี้มันน่ารำคาญยิ่งกว่าที่คุณต้องใช้ jiggery-pokery เหมือนข้างบนในการสืบค้น LINQ ที่เรียบร้อย
นอกจากนี้ยังน่ารำคาญที่Add<TableName>Rowเมธอดที่สร้างขึ้นนั้นไม่สามารถเข้าใจได้ในทำนองเดียวกันกับความคิดของประเภทที่เป็นโมฆะ ยิ่งไปกว่านั้นเนื่องจากTableAdapterเมธอดที่สร้างขึ้นไม่ใช่
NET ไม่มากนักที่ทำให้ฉันรู้สึกเหมือนที่ทีมนักพัฒนาพูดว่า "โอเคพวกเราสนิทกันมากพอแล้วจัดส่ง!" แต่สิ่งนี้แน่นอน
DBNull.Valueเมื่อnullตัวมันเองมีค่าเพียงพอที่จะเป็นตัวแทนของ NULL โชคดีที่ LINQ-to-SQL ใช้ null สำหรับ NULL
แก้ไข
5. สิ่งที่น่ารำคาญอีกประการหนึ่งของฉันคือวิธี System.Reflection.BindingFlags มีการใช้งานที่แตกต่างกันขึ้นอยู่กับวิธีการที่คุณใช้ ใน FindFields เช่น CreateInstance หรือ SetField หมายถึงอะไร? นี่เป็นกรณีที่พวกเขาใช้ความหมายเบื้องหลังการแจงนับนี้มากเกินไปซึ่งทำให้สับสน
ฉันไม่รู้ว่าฉันจะพูดได้ไกลถึงขนาดที่ว่ามันเป็นข้อบกพร่องในการออกแบบ แต่มันจะดีมากถ้าคุณสามารถสรุปการแสดงออกของแลมบ์ดาในลักษณะเดียวกับที่คุณสามารถทำได้ใน VB:
VB:
Dim a = Function(x) x * (x - 1)
ค#
คงจะดีถ้าสามารถทำได้:
var a = x => x * (x - 1);
แทนที่จะต้องทำสิ่งนี้:
Func<int, int> a = x => x * (x - 1);
ฉันรู้ว่ามันไม่ได้ยาวกว่านี้มากนัก แต่ใน Code Golf ทุกตัวละครนับว่าเหี้ย! พวกเขาไม่คำนึงถึงเรื่องนี้เมื่อออกแบบภาษาโปรแกรมเหล่านี้หรือไม่? :)
(int x) => x * (x -1);อาจหมายถึงFunc<int, int>หรืออาจหมายถึงExpression<Func<int, int>>
+ไม่ได้กำหนดไว้สำหรับobject.
System.Objectระดับ:
Equals และ GetHashCode - ไม่ใช่ทุกคลาสที่สามารถเทียบเคียงได้หรือแฮชได้ควรย้ายไปที่อินเทอร์เฟซ IEquatable หรือ IComparable (หรือคล้ายกัน) อยู่ในใจ
ToString - ไม่ใช่ทุกคลาสที่สามารถแปลงเป็นสตริงได้ควรย้ายไปที่อินเทอร์เฟซ IFormattable (หรือคล้ายกัน) อยู่ในใจ
ICollection.SyncRootทรัพย์สิน:
Generics ควรมีมาตั้งแต่แรก:
EqualityComparer<T>.Defaultอย่างถูกต้อง จากนั้นทั้งสองvar dict = new Dictionary<object, string>(EqualityComparer<object>.Default)และvar dict = new Dictionary<object, string>()จะใช้การเปรียบเทียบอ้างอิง / ความเท่าเทียมกัน
EqualityComparer<T>.Defaultไม่ ไม่จำเป็นต้องตรวจสอบทุกการค้นหา ตัวเปรียบเทียบเป็นคุณสมบัติของDictionaryอินสแตนซ์และแต่ละตัวจะDictionaryรู้ว่ากำลังใช้ตัวเปรียบเทียบใด
สิ่งหนึ่งที่ทำให้ฉันหงุดหงิดคือPredicate<T> != Func<T, bool>ความขัดแย้ง ทั้งคู่เป็นผู้รับมอบสิทธิ์ประเภทเดียวกันT -> boolแต่ไม่สามารถใช้งานได้
บางคน (ISV) ต้องการให้คุณสามารถคอมไพล์เป็นรหัสเครื่องในเวลาสร้างและเชื่อมโยงเพื่อสร้างไฟล์ปฏิบัติการแบบเนทีฟที่ไม่จำเป็นต้องใช้ dotNet
เรารู้มากเกี่ยวกับสิทธิเทคนิค OO ที่การแยกส่วนการเขียนโปรแกรมตามสัญญาการหลีกเลี่ยงการถ่ายทอดทางพันธุกรรมที่ไม่เหมาะสมการใช้ข้อยกเว้นที่เหมาะสมการเปิด / ปิดหลักความสามารถในการทดแทน Liskov เป็นต้น อย่างไรก็ตาม. Net frameworks ไม่ได้ใช้แนวทางปฏิบัติที่ดีที่สุด
สำหรับฉันข้อบกพร่องที่ใหญ่ที่สุดเพียงข้อเดียวในการออกแบบ. Net ไม่ได้ยืนอยู่บนไหล่ของยักษ์ใหญ่ การส่งเสริมกระบวนทัศน์การเขียนโปรแกรมในอุดมคติน้อยกว่าสำหรับกลุ่มโปรแกรมเมอร์จำนวนมากที่ใช้กรอบการทำงานของตนการส่งเสริมน้อยกว่ากระบวนทัศน์การเขียนโปรแกรมที่เหมาะที่จะฝูงของโปรแกรมเมอร์ที่ใช้กรอบของพวกเขา
หาก MS ให้ความสำคัญกับสิ่งนี้โลกวิศวกรรมซอฟต์แวร์อาจก้าวกระโดดอย่างมากในด้านคุณภาพความเสถียรและความสามารถในการปรับขนาดได้ในทศวรรษนี้ แต่อนิจจาดูเหมือนว่าจะถดถอย
ฉันไม่ชอบคำสั่งสวิตช์ C #
ฉันต้องการอะไรเช่นนี้
switch (a) {
1 : do_something;
2 : do_something_else;
3,4 : do_something_different;
else : do_something_weird;
}
จึงไม่มีการหยุดพักอีกต่อไป (ลืมง่าย) และความเป็นไปได้ในการคั่นค่าต่างๆ
switchโดยพื้นฐานแล้วใช้งานไม่ได้ในทุกภาษาที่เลียนแบบเวอร์ชันพิการโดยเจตนาของ C (ปรับให้เหมาะสมเพื่อความเร็ว!) VB มีค่าโดยสารที่ดีกว่ามาก แต่ก็ยังคงล้าหลังภาษาที่มีรูปแบบตรงกัน (Haskell, F # …)
เหตุการณ์ใน C # ที่คุณต้องตรวจสอบผู้ฟังอย่างชัดเจน นั่นไม่ใช่ประเด็นของเหตุการณ์ที่จะถ่ายทอดให้ใครก็ตามที่อยู่ที่นั่น? แม้ว่าจะไม่มี?
พฤติกรรมที่น่ากลัว (และค่อนข้างมองไม่เห็นสำหรับคนส่วนใหญ่) O (N ^ 2) ของตัวทำซ้ำแบบซ้อน / เรียกซ้ำ iterators
ฉันค่อนข้างเสียใจที่พวกเขารู้เรื่องนี้รู้วิธีแก้ไขแต่ไม่ได้มองว่ามีความสำคัญเพียงพอต่อการรวมบุญ
ฉันทำงานกับโครงสร้างแบบต้นไม้ตลอดเวลาและต้องแก้ไขโค้ดของคนที่ฉลาดเป็นอย่างอื่นเมื่อพวกเขาแนะนำการดำเนินการที่มีราคาแพงโดยไม่ได้ตั้งใจด้วยวิธีนี้
ความสวยงามของ "yield foreach" คือไวยากรณ์ที่เรียบง่ายและง่ายกว่าจะส่งเสริมโค้ดที่ถูกต้องและมีประสิทธิภาพนี่คือ"หลุมแห่งความสำเร็จ"ที่ฉันคิดว่าพวกเขาควรปรารถนาก่อนที่จะเพิ่มคุณสมบัติใหม่เพื่อความสำเร็จในระยะยาวของแพลตฟอร์ม
บางคลาสใช้อินเทอร์เฟซ แต่ไม่ได้ใช้หลายวิธีของอินเทอร์เฟซนั้นตัวอย่างเช่น Array ใช้ IList แต่ 4 ใน 9 วิธีทำให้ NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members .aspx
สมาชิกคงที่และประเภทที่ซ้อนกันในอินเทอร์เฟซ
นี้จะเป็นประโยชน์อย่างยิ่งเมื่อสมาชิกอินเตอร์เฟซที่มีพารามิเตอร์ของชนิดที่เป็นเฉพาะกับอินเตอร์เฟซ ( เช่นenum ) มันจะเป็นการดีที่จะซ้อนประเภท enum ในประเภทอินเทอร์เฟซ
ลักษณะเริ่มต้นของเหตุการณ์ที่อันตรายอย่างยิ่ง ความจริงที่ว่าคุณสามารถโทรหาเหตุการณ์และอยู่ในสถานะที่ไม่สอดคล้องกันเนื่องจากการที่สมาชิกถูกลบนั้นเป็นเรื่องที่น่าสยดสยอง ดูบทความที่ยอดเยี่ยมของJon SkeetและEric Lippertสำหรับการอ่านเพิ่มเติมในหัวข้อนี้
null ทุกที่.
const ไม่มีที่ไหนเลย
API ไม่สอดคล้องกันเช่นการเปลี่ยนผลตอบแทนอาร์เรย์voidแต่ผนวกกับStringBufferผลตอบแทนที่ไม่แน่นอนเดียวกันStringBufferผลตอบแทนที่ไม่แน่นอนเหมือนกัน
อินเทอร์เฟซการรวบรวมไม่เข้ากันกับโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปเช่นAddในSystem.Collections.Generic.IList<_>ไม่สามารถกลับผล
ไม่ต้องพิมพ์โครงสร้างเพื่อให้คุณเขียน System.Windows.Media.Effects.SamplingMode.BilinearBilinearแทนเพียง
ไม่แน่นอน IEnumeratorstructอินเตอร์เฟซที่ใช้งานโดยการเรียนเมื่อมันควรจะเป็นไม่เปลี่ยนรูป
ความเท่าเทียมกันและการเปรียบเทียบสินค้าเป็นระเบียบ: คุณได้มีSystem.IComparableและEqualsแต่แล้วคุณได้ยังSystem.IComparable<_>, System.IEquatable,System.Collections.IComparer , System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable, และSystem.Collections.Generic.IComparerSystem.Collections.Generic.IEqualityComparer
Tuples ควรเป็นโครงสร้าง แต่โครงสร้างจะยับยั้งการกำจัดการเรียกหางโดยไม่จำเป็นดังนั้นประเภทข้อมูลพื้นฐานและข้อมูลพื้นฐานส่วนใหญ่จะจัดสรรโดยไม่จำเป็นและทำลายความขนานที่ปรับขนาดได้
IEnumeratorอย่างไร?
0 แสงจันทร์เป็น enum
ลักษณะเฉพาะของ enum: http://blogs.msdn.com/abhinaba/archive/2007/01/09/more-peculiarites-of-enum.aspx
ดังที่แสดงโดยตัวอย่างที่ดีนี้: http://plus.kaist.ac.kr/~shoh/postgresql/Npgsql/apidocs/Npgsql.NpgsqlParameterCollection.Add_overload_3.html
คำแนะนำของฉันใส่เครื่องหมาย "@" เพื่อประโยชน์:
แทน:
ถ้า ((myVar & MyEnumName.ColorRed)! = 0)
ใช้สิ่งนี้:
ถ้า ((myVar & MyEnumName.ColorRed)! = @ 0)
หากต้องการเพิ่มคะแนนดี ๆ ที่ผู้อื่นทำไว้แล้ว:
DateTime.Now == DateTime.Now ส่วนใหญ่ แต่ไม่ใช่ทุกกรณี
Stringซึ่งไม่เปลี่ยนรูปมีตัวเลือกมากมายสำหรับการก่อสร้างและการจัดการ แต่StringBuilder(ซึ่งไม่แน่นอน) ไม่ได้
Monitor.EnterและMonitor.Exitควรเป็นวิธีการอินสแตนซ์ดังนั้นแทนที่จะสร้างออบเจ็กต์เฉพาะสำหรับการล็อกคุณสามารถสร้าง a ใหม่Monitorและล็อกสิ่งนั้นได้
ผู้ทำลายไม่ควรได้รับการตั้งชื่อว่าผู้ทำลาย ข้อมูลจำเพาะของ ECMA เรียกพวกมันว่า finalizers ซึ่งสร้างความสับสนน้อยกว่าสำหรับฝูง C ++ แต่ข้อกำหนดภาษายังคงอ้างถึงพวกเขาว่าเป็นตัวทำลาย
DateTime.Nowหนึ่งที่เห็นได้ชัดที่สุดคือการแข่งขันสภาพของโลก แต่ +1 สำหรับส่วนที่เหลือ
วิธีที่เราใช้คุณสมบัติทำให้ฉันระคายเคืองในบางครั้ง ฉันชอบคิดว่ามันเทียบเท่ากับเมธอด getFoo () และ setFoo () ของ Java แต่พวกเขาไม่ได้
หากแนวทางการใช้พร็อพเพอร์ตี้ระบุว่าคุณสมบัติควรสามารถตั้งค่าในลำดับใดก็ได้เพื่อให้การทำให้เป็นอนุกรมสามารถทำงานได้ก็จะไม่มีประโยชน์สำหรับการตรวจสอบเวลาที่กำหนด หากคุณมาจากพื้นหลังที่คุณต้องการป้องกันไม่ให้วัตถุปล่อยให้ตัวเองเข้าสู่สถานะที่ไม่ถูกต้องคุณสมบัติจะไม่ใช่วิธีแก้ปัญหาของคุณ บางครั้งฉันก็ไม่เห็นว่าพวกเขาดีกว่าสมาชิกสาธารณะอย่างไรเนื่องจากเรามีข้อ จำกัด ในสิ่งที่เราควรจะเป็นทำในอสังหาริมทรัพย์
ด้วยเหตุนี้ฉันจึงปรารถนาเสมอ (นี่ส่วนใหญ่คิดออกมาดัง ๆ ฉันแค่หวังว่าฉันจะทำอะไรแบบนี้ได้) ที่ฉันสามารถขยายไวยากรณ์คุณสมบัติได้ ลองนึกภาพดังนี้:
private string password;
public string Password
{
// Called when being set by a deserializer or a persistence
// framework
deserialize
{
// I could put some backward-compat hacks in here. Like
// weak passwords are grandfathered in without blowing up
this.password = value;
}
get
{
if (Thread.CurrentPrincipal.IsInRole("Administrator"))
{
return this.password;
}
else
{
throw new PermissionException();
}
}
set
{
if (MeetsPasswordRequirements(value))
{
throw new BlahException();
}
this.password = value;
}
serialize
{
return this.password;
}
}
ฉันไม่แน่ใจว่ามันมีประโยชน์หรือสิ่งที่เข้าถึงสิ่งเหล่านี้จะเป็นอย่างไร แต่ฉันแค่หวังว่าฉันจะทำประโยชน์ได้มากกว่านี้กับคุณสมบัติและปฏิบัติต่อพวกเขาจริงๆเช่น get and set method
วิธีการขยายเป็นวิธีที่ดี แต่เป็นวิธีที่น่าเกลียดในการแก้ปัญหาที่สามารถแก้ไขได้สะอาดกว่าด้วยส่วนผสมจริง (ดูทับทิมเพื่อดูสิ่งที่ฉันกำลังพูดถึง) ในเรื่องของมิกซ์อิน วิธีที่ดีมากในการเพิ่มลงในภาษาคือการอนุญาตให้ใช้ชื่อสามัญในการสืบทอด สิ่งนี้ช่วยให้คุณสามารถขยายคลาสที่มีอยู่ในลักษณะเชิงวัตถุที่ดี:
public class MyMixin<T> : T
{
// etc...
}
สามารถใช้เช่นนี้เพื่อขยายสตริงเช่น:
var newMixin = new MyMixin<string>();
วิธีนี้มีประสิทธิภาพมากกว่าวิธีการขยายเนื่องจากช่วยให้คุณสามารถแทนที่เมธอดตัวอย่างเช่นการรวมวิธีการที่อนุญาตให้มีฟังก์ชันคล้าย AOP ภายในภาษา
ขออภัยที่ :-) พูดจาโผงผาง
Microsoft จะไม่แก้ไขข้อบกพร่องที่เห็นได้ชัดในกรอบงานและจะไม่ให้ hooks เพื่อให้ผู้ใช้สามารถแก้ไขได้
นอกจากนี้ยังไม่มีวิธีในการดำเนินการไบนารีแพทช์. NET ที่รันไทม์และไม่มีวิธีระบุไลบรารีเฟรมเวิร์ก. NET เวอร์ชันส่วนตัวโดยไม่ต้องแพทช์ไบนารีไลบรารีเนทีฟ (เพื่อสกัดกั้นการเรียกโหลด) และ ILDASM ไม่สามารถแจกจ่ายซ้ำได้ดังนั้นฉันจึงไม่สามารถทำให้เป็นอัตโนมัติได้ แพทช์ต่อไป
สามารถเรียกใช้เมธอดส่วนขยายบนตัวแปร null ได้เช่น
วัตถุ a = null; ก. MyExtMethod (); // สิ่งนี้เรียกได้สมมติว่ามีการกำหนด MyExtMethod ไว้ที่ไหนสักแห่ง
อาจเป็นประโยชน์ แต่มีความคลุมเครือในหัวข้อข้อยกเว้นการอ้างอิงที่เป็นโมฆะ
การตั้งชื่อ 'ข้อบกพร่อง' อย่างหนึ่ง "C" ของ "configuration" ใน System.configuration.dll ควรเป็นตัวพิมพ์ใหญ่
การจัดการข้อยกเว้น ข้อยกเว้นควรถูกจับหรือโยนทิ้งเหมือนใน Java คอมไพลเลอร์ควรตรวจสอบในเวลาคอมไพล์ ผู้ใช้ไม่ควรพึ่งพาความคิดเห็นสำหรับข้อมูลข้อยกเว้นภายในการร้องขอเป้าหมาย
เมธอด. พารามิเตอร์. Add () บน SqlCommand ใน V1 ของเฟรมเวิร์กได้รับการออกแบบอย่างน่ากลัว - หนึ่งในโอเวอร์โหลดโดยทั่วไปจะไม่ทำงานหากคุณส่งผ่านพารามิเตอร์ที่มีค่า (int) เป็น 0 ซึ่งทำให้พวกเขาสร้าง วิธีการ. Parameters.AddWithValue () บนคลาส SqlCommand
ICollection<T>และIList<T>; อย่างน้อยที่สุดก็คืออินเทอร์เฟซคอลเลกชันแบบอ่านอย่างเดียวที่เป็นโควาเรียนIListSource<out T> (ที่มีตัวแจงนับดัชนีและตัวนับ) จะมีประโยชน์อย่างยิ่งTransform(Sequence<T>, Func<T,T>)ฟังก์ชันที่จำเป็นในการตรวจสอบอย่างรวดเร็วว่าฟังก์ชันนั้นส่งคืนค่าเดียวกันหรือค่าที่แตกต่างกัน หากฟังก์ชันไม่แก้ไขอาร์กิวเมนต์ส่วนใหญ่ / ทั้งหมดลำดับเอาต์พุตสามารถแชร์หน่วยความจำบางส่วน / ทั้งหมดจากลำดับอินพุตได้ หากไม่มีความสามารถในการเปรียบเทียบค่าประเภท T แบบบิตต้องใช้การเปรียบเทียบที่ช้ากว่ามากซึ่งส่งผลเสียต่อประสิทธิภาพอย่างมากList<T>ไปยังสมมุติฐานIListSource<U>(โดยที่ T: U) แม้ว่าคลาสจะไม่ได้ใช้อินเทอร์เฟซนั้นอย่างชัดเจน มีไลบรารีที่แตกต่างกันอย่างน้อยสาม ไลบรารี (เขียนขึ้นโดยอิสระ) เพื่อจัดหาฟังก์ชันนี้ (แน่นอนว่ามีข้อบกพร่องด้านประสิทธิภาพ - หากวิธีแก้ปัญหาที่สมบูรณ์แบบเป็นไปได้มันจะไม่ยุติธรรมที่จะเรียกว่าข้อบกพร่องใน. NET) WeakReference<T>(คุณสามารถเขียนของคุณเองได้อย่างง่ายดาย แต่จะใช้การร่ายภายใน)Predicate<T>เทียบกับFunc<T,bool>) ฉันมักจะหวังว่าเราจะมีการพิมพ์โครงสร้างสำหรับอินเทอร์เฟซและผู้รับมอบสิทธิ์เพื่อให้เกิดการมีเพศสัมพันธ์ที่หลวมขึ้นระหว่างคอมโพเนนต์เนื่องจากใน. NET นั้นไม่เพียงพอสำหรับคลาสใน DLL อิสระที่จะใช้อินเทอร์เฟซเดียวกัน - พวกเขาต้องแบ่งปันการอ้างอิงร่วมกันไปยังส่วนที่สามด้วย DLL ที่กำหนดอินเทอร์เฟซDBNull.Valueมีอยู่แม้ว่าnullจะทำหน้าที่ในจุดประสงค์เดียวกันได้ดีพอ ๆ กันvariable = variable ?? valueคุณต้องเขียน มีอยู่ไม่กี่แห่งใน C # ที่ขาดความสมมาตรโดยไม่จำเป็น ตัวอย่างเช่นคุณสามารถเขียนif (x) y(); else z();(โดยไม่ต้องวงเล็บ) try y(); finally z();แต่คุณไม่สามารถเขียนIList<T>แต่ฉันต้องการใช้และIReadableByIndex<out T> IAppendable<in T>สิ่งอื่น ๆ อีกมากมายของคุณเป็นเรื่องที่น่าผิดหวังที่ฉันเห็นด้วยเช่นกัน
IListReader<T>;) - ฉันใช้คำว่า "source" เป็นคำตรงข้ามของ "sink" (อินเทอร์เฟซสำหรับเขียนอย่างเดียว)
IListSource<in T>หรือIReadableList<out T>. อาจมีค่าในการมีประเภทอินเทอร์เฟซพื้นฐานรวมถึงวิธีการที่ไม่มีอยู่ในอนุพันธ์ทั้งหมดแม้ว่าฉันคิดว่าบ่อยครั้งที่อินเทอร์เฟซจะค่อนข้างเชี่ยวชาญ ตัวอย่างเช่นอาจมีIList<T>วิธีการปรับขนาดซึ่งอาจได้ผลหรือไม่ได้ผลและIResizableList<T>ใช้วิธีการเดียวกัน แต่รับประกันว่าควรใช้งานได้ แนวทางดังกล่าวอาจมีประโยชน์ในกรณีที่เขตข้อมูลอาจมีการอ้างอิงเพียงรายการเดียวที่ยังหลงเหลืออยู่ไปยังรายการที่ไม่แน่นอนหรือการอ้างอิงที่ใช้ร่วมกันไปยังรายการที่ไม่เปลี่ยนรูป
if (rl:(list as IResizableList<T>) != null) rl.Add(...);แต่มีข้อเสนออื่น ๆ ในฐานะผู้เขียนคอลเลกชั่นและอะแดปเตอร์คอลเลกชันต่างๆสิ่งที่ทำให้ฉันรำคาญคือการเขียนวิธีการหลอกๆมากมายที่ทำให้เกิดข้อยกเว้น ในฐานะแฟนความปลอดภัยฉันไม่อยากได้รับอนุญาตให้เรียกวิธีการที่ผิดกฎหมาย แฟน IntelliSense ฉันไม่ต้องการเห็นพวกเขาอยู่ในรายการ
สิ่งหนึ่งที่ฉันออก ticked ใน 1.x คือเมื่อใช้System.Xml.XmlValidatingReaderที่ValidationEventHandler's ValidationEventArgsไม่เปิดเผยพื้นฐานXmlSchemaException(ทำเครื่องหมายภายใน) ซึ่งมีข้อมูลทั้งหมดที่มีประโยชน์เช่นและlinenumber positionแต่คุณคาดว่าจะแยกวิเคราะห์สิ่งนี้ออกจากคุณสมบัติสตริงข้อความหรือใช้การสะท้อนเพื่อขุดออก ไม่ดีนักเมื่อคุณต้องการส่งคืนข้อผิดพลาดที่ผ่านการทำความสะอาดแล้วให้กับผู้ใช้
ไม่ชอบที่คุณไม่สามารถใช้ค่าของ enum หนึ่งใน enum อื่นได้เช่น:
enum Colors { white, blue, green, red, black, yellow }
enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow }
typeof(Color)! = typeof(SpecialColors).
enum SpecialColors { blue = Colors.blue, red = Colors.red, yellow = Colors.Yellow }
ตัวแปรที่พิมพ์โดยนัยถูกนำไปใช้ IMO ไม่ดี ฉันรู้ว่าคุณควรใช้มันเมื่อทำงานกับนิพจน์ Linq เท่านั้น แต่มันน่ารำคาญที่คุณไม่สามารถประกาศนอกขอบเขตท้องถิ่นได้
จาก MSDN:
เหตุผลที่ฉันคิดว่ามันเป็นการใช้งานที่ไม่ดีก็คือพวกเขาเรียกมันว่า var แต่มันก็ยังห่างไกลจากการเป็นตัวแปร มันเป็นเพียงไวยากรณ์ชวเลขที่ไม่ต้องพิมพ์ชื่อคลาสเต็ม (ยกเว้นเมื่อใช้กับ Linq)