C # (.NET) Tasarım Kusurları [kapalı]


84

Genel olarak C # veya .NET Framework'teki en büyük tasarım kusurlarından bazıları nelerdir?

Örnek: boş değer atanamaz bir dize türü yoktur ve bir IDataReader'dan değerleri alırken DBNull'u kontrol etmeniz gerekir.


Bu tasarım kusurları ne anlamda?
Juliet

IDataReader ile manuel olarak kontrol etmek yerine IsDBNull'u kullanabilirsiniz
Marc Gravell

9
Cue Jon Skeet kapalı sınıflar hakkında konuşacak;)
johnc

3
IDataReader'ı bir uzantı yöntemiyle düzeltmek oldukça kolaydır: bkz. Weblogs.asp.net/skillet/archive/2008/06/18/… .
Robert Rossney

@lagerdalek - Mümkünse bu yorumu + 1'leyeceğim; iyi hatırlanmış
Marc Gravell

Yanıtlar:


39

Bu gönderiye kesinlikle katılıyorum (ToString eksikliğini dışlayanlar için, sınıfınız için özel bir format sağlamak için bir hata ayıklayıcı özelliği vardır).

Yukarıdaki listenin üstüne, aşağıdaki makul istekleri de eklerim:

  1. null yapılabilir değer türlerinin tamamlayıcısı olarak null yapılamayan başvuru türleri,
  2. bir yapının boş yapıcısının geçersiz kılınmasına izin verin,
  3. genel tür kısıtlamalarının mühürlenmiş sınıfları belirtmesine izin verin,
  4. Kısıtlama olarak kullanıldığında keyfi kurucu imzaları talep eden başka bir postere katılıyorum, yani. nerede T : new(string)veya neredeT : new(string, int)
  5. Ayrıca, hem boş olay listeleri hem de eşzamanlı ortamda olayların düzeltilmesi hakkında başka bir postere katılıyorum (ikincisi zor olsa da),
  6. operatörler sınıfın statik metotları olarak değil (veya en azından sadece statik metotlar olarak değil) genişletme metotları olarak tanımlanmalıdır,
  7. arayüzler için statik özelliklere ve yöntemlere izin verin (Java'da buna sahiptir, ancak C # yoktur),
  8. nesne başlatıcılarda olay başlatmaya izin ver (şu anda yalnızca alanlara ve özelliklere izin verilmektedir),
  9. "nesne başlatıcı" sözdizimi neden yalnızca bir nesne oluştururken kullanılabilir? Neden herhangi bir zamanda kullanıma sunmuyorsunuz, yani.var e = new Foo(); e { Bar = baz };
  10. ikinci dereceden numaralandırılabilir davranışı düzeltmek ,
  11. tüm koleksiyonların yineleme için değişmez anlık görüntüleri olmalıdır (yani, koleksiyonun değiştirilmesi yineleyiciyi geçersiz kılmamalıdır),
  12. Tuplelar eklemek kolaydır, ancak " Either<T>" gibi verimli bir kapalı cebirsel tip değildir, bu nedenle kapalı bir cebirsel tip beyan etmenin ve üzerinde kapsamlı desen eşleştirmesinin uygulanmasının bir yolunu çok isterim (temelde ziyaretçi modeli için birinci sınıf destek, ancak çok daha verimli); bu nedenle yalnızca numaralandırmaları alın, kapsamlı kalıp eşleştirme desteğiyle genişletin ve geçersiz vakalara
  13. Genel olarak örüntü eşleştirmeyi desteklemek isterdim, ancak en azından nesne türü testi için; Ayrıca burada başka bir gönderide önerilen anahtar sözdizimini de beğeniyorum,
  14. System.IOSınıfların Streambiraz kötü tasarlandığına dair başka bir gönderiye katılıyorum ; bazı uygulamaların atılmasını gerektiren herhangi bir arayüz NotSupportedExceptionkötü bir tasarımdır,
  15. IListolduğundan çok daha basit olmalı; aslında, bu gibi beton toplama arayüzleri çoğu için doğru olabilir ICollection,
  16. örneğin IDictionary gibi çok fazla yöntem istisnalar atar,
  17. Java'da mevcut olandan daha iyi bir tür kontrol edilmiş istisna tercih ederim (bunun nasıl yapılacağı için tür ve etki sistemleri araştırmasına bakın),
  18. genel yöntem aşırı yük çözümlemesinde çeşitli can sıkıcı köşe durumlarını düzeltin; örneğin, biri başvuru türlerinde, diğeri null yapılabilir yapı türlerinde çalışan iki aşırı yüklenmiş uzantı yöntemi sağlamayı deneyin ve tür çıkarımınızın bunu nasıl sevdiğini görün,
  19. INotifyPropertyChangedalan adını bir dizge olarak alan gibi arabirimler için alan ve üye adlarını güvenli bir şekilde yansıtmanın bir yolunu sağlar ; Bunu, lambda'yı a MemberExpression, ie ile alan bir genişletme yöntemi kullanarak yapabilirsiniz . () => Foo, ancak bu çok verimli değil
    • Güncelleme: C # 6.0 nameof(), tek üye adları için işleci ekledi , ancak jeneriklerde çalışmıyor ( nameof(T) == "T"gerçek tür bağımsız değişkeninin adı yerine: yine de yapmanız gerekiyor typeof(T).Name) - ne de bir "yol" dizesi almanıza izin vermiyor , örneğin nameof(this.ComplexProperty.Value) == "Value"olası uygulamalarını sınırlama.
  20. arabirimlerde operatörlere izin verin ve tüm çekirdek numara türlerini uygulayın IArithmetic; diğer kullanışlı paylaşımlı operatör arayüzleri de mümkündür,
  21. nesne alanlarının / özelliklerinin değiştirilmesini zorlaştırın veya en azından, değişmez alanlara açıklama eklemeye izin verin ve tür denetleyicinin onu zorlamasını sağlayın (sadece hatıra özelliği olarak davranın, çünkü bu zor değil!); aslında, alanları ve özellikleri daha mantıklı bir şekilde birleştirin çünkü ikisine birden sahip olmanın bir anlamı yok; C # 3.0'ın otomatik özellikleri bu yöndeki ilk adımdır, ancak yeterince ileri gitmezler,
    • Güncelleme: C # readonlyanahtar kelimesine sahipken ve C # 6.0 salt okunur otomatik özellikler eklerken, değişmez türler ve değerler için gerçek dil desteği kadar katı değildir.
  22. yapıcıları bildirmeyi basitleştirin; F # 'ın yaklaşımını seviyorum, ancak buradaki sınıf adı yerine basitçe "yeni" gerektiren diğer gönderi en azından daha iyi,

Sanırım şimdilik bu kadar yeter. Bunların hepsi geçen hafta karşılaştığım rahatsızlıklar. Gerçekten aklımı koyarsam muhtemelen saatlerce devam edebilirim. C # 4.0 zaten kesinlikle onayladığım adlandırılmış, isteğe bağlı ve varsayılan bağımsız değişkenler ekliyor.

Şimdi mantıksız bir istek için:

  1. o olurdu , gerçekten, gerçekten C # / CLR yani cinsi yapıcı polimorfizm ve destek olabilir güzel. jenerikler üzerinde jenerikler,

Güzel lütfen? :-)


1
# 1 ile ilgili olarak, her tür ya bir varsayılan değere sahip olmalıdır ya da sistem, belirli bir türden bir değişken veya alan tahsis edildiğinde bir kurucuyu çalıştırmanın bir yolunu sağlamalıdır. İkincisini (# 2'nin bir dalı) tercih ederim, ancak sanal olmayan yöntemler / özellikler, boş kontrol olmadan çağrılmaları gerektiğini belirtmek için dekore edilebilirse, # 1 barındırılabilir. Bu, "Dize" alanları gibi şeylerin boş bir dizge yerine varsayılan olarak davranmasına izin verir (çünkü String'in statik "uzunluk" işlevi boş bir dizede çağrılırsa 0 döndürebilir).
supercat

1
# 2 ile ilgili olarak, yapıların yalnızca sıfırla doldurma dışında bir kurucu değil, aynı zamanda bayt için kopya dışında bir kopya oluşturucuyu da belirtebilmesi genel olarak yararlı olacaktır. Aslında, varlıkların değer veya referans anlambilimine sahip olabileceğini ve değer türü yığın nesnelerinin değişken, paylaşımlı-değişmez veya taahhüt edilmemiş olarak etiketlenmesine izin veren iyi bir iş çıkarabilecek .net benzeri bir çerçeve görmek isterim. (taahhüt edilmemiş bir değer nesnesi, kipi ilk olarak CompareExchange'd ise mutasyona uğratılabilir veya kipi ilk önce CompareExchange'd ile paylaşılıyorsa paylaşılabilir).
supercat

1
Mükemmel puanlar! # 1 için, bir tür sistemi ek açıklaması yaygın çözümdür, ancak T: new () gibi tür değişkenleri aracılığıyla kurucu kısıtlamalarının yayılmasını tercih ederim. Re: # 2, kopya oluşturucular hakkında iyi bir nokta, ancak yukarıda anlattığım satırlar boyunca daha genel kuruculardan memnun olurum. Daha da iyisi, farklı yöntemler olarak kurucuları tamamen ortadan kaldırmak ve basitçe onları statik yöntemler haline getirmek olacaktır. Bu, özellikle arayüzlerde statik yöntemlere izin verirsek, daha basit ve daha genel yapı modellerine izin verir. Statik yöntemler olarak oluşturucular + arabirimlerde statik yöntemler de # 1'i çözer.
naasking

3
# 3: Mühürlenmiş bir sınıfı genel bir tür parametresi olarak kullanmanın anlamı nedir, örneğin Foo <T> burada T: string? 11: Tamam, yani List<T>bir milyon T'ye sahibim . Anlık görüntünün verimli bir şekilde çekilmesini nasıl önerirsiniz? # 21: readonly.... anahtar kelimesini kullanın, burada bazı iyi öneriler varken, bunlar çoğunlukla sadece önerilerdir, tasarım kusurları değil.
Qwertie

2
Bu çok ilginç bir cevap ama bence C # 6 özellikleriyle güncellemeliyiz. Örnek: 19. ve 21. maddeler uygulandı =)
eduardobr

72
  • Reset()üzerinde yöntem IEnumerator<T>yineleyici blokları, dilin Spec için bile bir hata (idi talepler bu bir istisna atar)
  • dizileri döndüren yansıma yöntemleri Eric'in görüşüne göre bir hataydı
  • dizi kovaryansı bir tuhaflıktı ve kalır
    • Güncelleştirme: .NET 4.0 ile C # 4.0, genel arabirimlere ortak değişken / kontravaryans desteği ekledi (gibi IEnumerable<out T>ve Func<in T, out TResult>ancak somut türler (gibi List<T>) değil.
  • ApplicationException yerine gözden düştü - bu bir hata mıydı?
  • senkronize koleksiyonlar - güzel bir fikir, ancak gerçekte yararlı olması gerekmez: genellikle birden çok işlemi senkronize etmeniz gerekir ( Containsdaha sonraAdd ), bu nedenle farklı işlemleri senkronize eden bir koleksiyon o kadar da kullanışlı olmaz
    • Güncelleme: tipleri ile , , vb .NET Framework 4.0 eklendi - fabrika temsilci kabul yöntemler fabrika sadece anahtar başına bir kere çağrılır garanti etmez bile.System.Collections.ConcurrentTryAddGetOrAddTryRemove
  • using/ lockşablonundan daha fazla yararlanılabilirdi - belki de yeniden kullanılabilir (genişletilebilir?) bir sözdizimini paylaşmalarına izin verebilirdi; bunu geri dönerek IDisposableve kullanarak simüle edebilirsiniz using, ancak daha net olabilirdi
  • yineleyici bloklar: argümanları önceden kontrol etmenin basit bir yolu yoktur (tembellikten ziyade). Elbette, iki zincirleme yöntem yazabilirsiniz, ancak bu çirkin
  • daha basit değişmezlik iyi olurdu; C # 4.0 biraz yardımcı olur , ancak yeterli değildir
  • hayır "bu ref-tipi parametre boş olamaz" desteği - ancak sözleşmeler (4.0'da) bu konuda yardımcı olur. Ancak sözdizimi Foo(SqlConnection! connection)(boş bir kontrol / ekleyen throw) güzel olurdu ( int?vb. Aksine )
  • operatörlerin ve varsayılan olmayan kurucuların jeneriklerle desteklenmemesi; C # 4.0 çözer bununla biraz dynamicya da etkinleştirebilirsiniz böyle
  • yineleyici değişkeni , genişletme sırasında süre dışında bildirilir foreach, yani anon-methodlar / lambdas, her yineleme için bir değişken yerine tek bir değişkeni yakalar (threading / async / etc ile ağrılı)

IEnumerable! = IEnumerable <nesne> gerçekten garip
Rauhotz

2
Eh, IEnumerable şey 1.1 akşamdan kalma; .Cast <object> () 'i LINQ ile kullanabilirsiniz, en azından
Marc Gravell

8
BCL çalışanları bunun ApplicationExceptionbir hata olduğunu söylediler - umdukları kadar yararlı değil. Bunun System.Exceptionolması gerektiğini de söylediler abstract.
Jay Bazuzi

2
Null yapılamaz: Normal bir referans türü T'yi boş olmayan bir T alan bir şeye geçirmek derleme hatası olmalıdır! (tıpkı int'i int'e geçiremeyeceğiniz gibi). Diğer yönden geçmek elbette iyidir.
Jay Bazuzi

1
@Jon Harrop: IMHO, değişmez diziler ve salt okunur dizi referansları için destek olmalıydı. Muhtemelen bazı diğer dizi varyantları için de (ör. "Yeniden boyutlandırılabilir dizi" (dolaylı referans) veya bir ofset ve sınır ile bir dizi referansı).
supercat

60

TextWriter a, temel StreamWriter'ın sınıfıdır. o ne lan?

Bu her zaman beni aşırı derecede karıştırır.


19
+1 Her seferinde bakmam gerekiyor. ( Ne demek oluyor ki TextWriter () 'ı yenileyemiyorum?)
Nicholas Piasecki

1
Tanrıya şükür ... Sadece ben olduğumu düşündüm.
IJ Kennedy

? Her zaman metin yazan bir şeydir, ancak bunu yalnızca StreamWriter bir akışa yapar. Oldukça basit görünüyor.
Jon Hanna

4
StreamWriter adı, metni yeterince açık bir IMO yazdığı gerçeğini yapmaz. Sadece bayt yazacakmış gibi ayrı bir ses çıkarır ve TextWriter, api'sini (string toWrite) sizin için bayta dönüştürecek süslü bir uygulama olacaktır. Şimdi eğer StreamTextWriter olarak adlandırılmış olsaydı, o zaman elbette, hemen açık ama biraz uzun olurdu :(
Quibblesome

44

Küçük bir C # pet peev - oluşturucular, oluşturucunun sınıfla aynı ada sahip olması için C ++ / Java sözdizimini kullanır.

New()ya ctor()da çok daha güzel olurdu.

Ve elbette, coderush gibi araçlar, sınıfları yeniden adlandırmak için bunu daha az sorun haline getirir, ancak okunabilirlik bakış açısıyla, New () büyük bir netlik sağlar.


Eee, neyin yeni bir örneğini yaratmaya çalıştığını nereden bilecek?
BlueRaja - Danny Pflughoeft

4
@BlueRaja: Scott, sınıflardaki kurucuların isimlendirilmesine atıfta bulunuyor. class Foo { new(int j) {i = j} int i; }
dalle

Ctor () veya constructor () 'ın daha iyi olacağı konusunda% 100 hemfikir olsam da ( New--uppercase olmayan anahtar kelimeler geleneğe aykırıdır), buna tasarım hatası demekten çekiniyorum. Mevcut C ++ / Java geliştiricilerini cezbetmek istediler ve birçok aptal eski sözdizimsel geleneği ödünç almak, muhtemelen hedeflerine ulaşmalarına yardımcı oldu.
Qwertie

bu soruyla ilgili: stackoverflow.com/questions/32101993/c-sharp-sorted-linkedlist , bir LinkedList'i MergeSort, bucketsort veya başka bir sıralama algoritması ile basitçe sıralamanın bir yolu (yalnızca .NET kullanarak), ancak çok daha yavaş olan linq kullanarak geçici bir uygulamadan daha fazla.
CoffeDeveloper

29

Yapamayacağını anlamıyorum

burada T: yeni (U)

Yani genel tip T'nin varsayılan olmayan bir kurucuya sahip olduğunu bildirirsiniz.

Düzenle:

Bunu yapmak istiyorum:

public class A 
{
    public A(string text) 
    {

    }
}


public class Gen<T> where T : new(string text) 
{

}

Genel bir tür, bu türdeki nesneleri, yani arayüzlerini nasıl kullanacağınızı belirtir. Yapıcı, tüketicinin ilgisi olmayan bu arayüzün bir uygulama ayrıntısıdır. Parametreli bir örnek oluşturmanız gerektiğinde, bir fabrika kullanın.
Bryan Watts

Bilgi için, derleme zamanı denetimi yapamasa da, MiscUtil'de varsayılan olmayan kurucuları (jeneriklerde) verimli bir şekilde, yani Activator.CreateInstance veya yansıma olmadan kullanmak için bazı kodlar vardır.
Marc Gravell

Çünkü hiçbir anlam ifade etmiyor ve bazı kullanımlarda kafa karıştırıcı. Bununla birlikte, değişmez nesnelerle çalışırken faydalı olabilir.
Pop Catalin

7
Genel olarak, üye kısıtlamalarının olmaması can sıkıcıdır, evet.
MichaelGG

3
Amin, bunu hep istemişimdir
Steve

20

Bundan ilk bahseden ben olduğuma gerçekten şaşırdım:

ADO.NET türlenmiş veri kümeleri, null yapılabilir türlerin özellikleri olarak null yapılabilir sütunları göstermez. Bunu yazabilmelisin:

int? i = myRec.Field;
myRec.Field = null;

Bunun yerine şunu yazmalısın ki bu çok aptalca:

int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field;
myRec.SetFieldNull();

Bu, .NET 2.0'da can sıkıcıydı ve güzel, düzgün LINQ sorgularınızda yukarıdaki gibi jiggery-pokery kullanmak zorunda olmanız şimdi daha da can sıkıcı.

Ayrıca, üretilen Add<TableName>Rowyöntemin null atanabilir türler kavramına benzer şekilde duyarsız olması da can sıkıcı . Her şey, oluşturulduğundan beriTableAdapter yöntemler öyle değil.

.NET'te geliştirme ekibinin "Tamam çocuklar, yeterince yakınız - gönderin!" Dediğini hissettiren pek bir şey yok. Ama bu kesinlikle öyle.


Tüm kalbimle katılıyorum! Bu, onu her kullanmam gerektiğinde beni rahatsız ediyor (ki bu genellikle). Argh! +1
Eyvind

2
Yapabilecekleri en az şey, baştan sona DBNull yerine boş değer atanabilir değer türlerini kullanan yeni bir DataSetV2 sınıfı (kötü ad - yalnızca bağımsız değişken uğruna) bulmak olabilirdi.
Christian Hayter

NULL'u temsil etmek için tamamen yeterli DBNull.Valueolduğunda , özel bir değer talep etmenin saçmalığını unutmayalım null. Neyse ki LINQ-to-SQL sadece NULL için null kullanır.
Qwertie

Aslında bu saçmalık, tüm saçma yapının üzerine inşa edildiği kayadır.
Robert Rossney

20
  1. Stream, StringWriter, StringReader, TextReader, TextWriter sınıflarının büyük bir hayranı değilim ... neyin ne olduğu sezgisel değil.
  2. IEnumerable.Reset yineleyiciler için bir istisna atma. Veritabanına bağlandığında her zaman sıfırlamayı çağıran bazı üçüncü taraf bileşenlerim var, bunları kullanmak için önce bir listeye yayınlamamı gerektiriyor.
  3. Xml Serializer, seri hale getirilmiş IDictionary öğelerine sahip olmalıdır
  4. HttpWebRequest & FTP API'yi tamamen unutmuşum, benim için ne acı .... (Nicholas'ın bana bunu hatırlatması için teşekkürler :-)

Edit
5. Benim bir başka sıkıntım da System.Reflection.BindingFlags'ın kullandığınız yönteme bağlı olarak farklı kullanımları olmasıdır. Örneğin FindFields'da CreateInstance veya SetField ne anlama geliyor? Bu, kafa karıştırıcı olan bu sayımın arkasındaki anlamı aşırı yükledikleri bir durum.


1
+1 Her seferinde XmlTextWriter, TextWriter vb. Sınıflardan herhangi birine bakmam gerekiyor. HttpWebRequest / Response öğesiyle aynı. Tamamen sezgisel olmayan API var.
Nicholas Piasecki

+ 1-1 = 0: XmlTextWriter vb. Adından ne olduklarını gerçekten anlamanın imkansız olduğuna katılıyorum. HttpWebRequest Katılmıyorum Bunu oldukça sezgisel buluyorum.
AnthonyWJones

Sanırım her biri için. Sanırım FTP ile sahip olduklarından daha yüksek düzeyde bir soyutlama beklerdim.
JoshBerke

15

Bunun bir tasarım hatası olduğunu söyleyecek kadar ileri gideceğimi bilmiyorum, ancak VB'de yapabildiğiniz gibi bir lambda ifadesini çıkarırsanız gerçekten güzel olurdu:

VB:

Dim a = Function(x) x * (x - 1)

C #

Bunu yapabilsem güzel olurdu:

var a = x => x * (x - 1);

Bunu yapmak zorunda kalmak yerine:

Func<int, int> a = x => x * (x - 1);

Çok uzun sürmediğinin farkındayım, ama Code Golf'te her karakterin önemi var! Bu programlama dillerini tasarlarken bunu hesaba katmıyorlar mı? :)


3
Microsoft, dilleri tasarlarken Code Golf'u dikkate almalı mı?
jrcs3

3
@Ray Burns: VB'de nasıl biliyor? VB bunu destekliyor, peki fark nedir?
BenAlabaster

3
@RayBurns Çıkarım yazın? Bunu 1989'dan beri kullanıyorum.
RD1

3
Lamdalar C # 'da Homoiconic'tir. parça bir (int x) => x * (x -1);anlam ifade edebilir Func<int, int>veya şu anlama gelebilirExpression<Func<int, int>>
Scott Weinstein

3
@BenAlabaster: VB, geç bağlanmış aritmetik operatörleri destekler. C # bunları derleme zamanında çözmelidir. Bu bir dil farkı. Örneğin, VB iki nesneyi birlikte ekleyebilir. C # +için tanımlanmadığı için olamaz object.
yinelemeli

14
  1. System.Object sınıfı:

    • Eşittir ve GetHashCode - tüm sınıflar karşılaştırılabilir veya karma hale getirilemez, bir arabirime taşınmalıdır. IEquatable veya IComparable (veya benzeri) akla geliyor.

    • ToString - tüm sınıflar bir dizeye dönüştürülemez, bir arabirime taşınmalıdır. IFormattable (veya benzeri) akla geliyor.

  2. ICollection.SyncRoot özelliği:

    • Kötü tasarımı teşvik eder, harici bir kilit neredeyse her zaman daha kullanışlıdır.
  3. Jenerikler başından beri orada olmalıydı:

    • System.Collections ad az ya da çok eskimiş sınıflar ve arayüzler bir sürü içerir.

1
1. Bu yöntemler o kadar yaygındır ki, tuhaf durumların iyi olduğuna karar verildi, sınıfınızda birisinin Object.Equals'ı çağırmasının bir önemi var mı? Bir uygulama olabileceği veya olmayabileceği biliniyor ve şunları gerektiriyor: IEquatable, IFormattable, sınıfların% 99'unda tuhaftır.
Guvante

1
Örneğin, kullanıcı tanımlı nesnelerin anahtar olarak olduğu bir sözlük oluşturabilmek, varsayılan referans eşitliğini kullanarak, bu amaç için kullanıcı tanımlı nesnelere açıkça kod eklemek zorunda kalmadan yararlıdır. Finalize'nin çok daha büyük bir israf olduğunu düşünürdüm (daha iyi bir alternatif, sonuçlandırmaya ihtiyaç duyan nesnelere iFinalizable'ı uygulamak ve sonuçlandırma için kendilerini açıkça kaydettirmek olabilirdi). OTOH, iDisposable için, bir yapıcı bir istisna atarsa ​​Dispose çağırmak da dahil olmak üzere daha doğal bir destek olmalıydı.
supercat

1
@supercat: Gerekli olan tek şey EqualityComparer<T>.Defaultdoğru şekilde güncellemektir . Sonra her ikisi de var dict = new Dictionary<object, string>(EqualityComparer<object>.Default)ve var dict = new Dictionary<object, string>()referans karşılaştırma / eşitlik kullanacaktır.
dalle

1
@supercat: Tarif ettiğiniz şey tam olarak ne işe EqualityComparer<T>.Defaultyaradığıdır. Her aramada kontrol etmeye gerek yok. Karşılaştırıcı, Dictionaryörnek için bir özelliktir ve her Dictionarybiri hangisini kullandığını bilir.
dalle

1
@supercat: Bir sözlük, anahtar olarak en genel türü (yani ortak temel sınıf) kullanmalıdır; aynı sözlükte hem Strings hem de DateTime kullanmak, kullanıcı tanımlı bir karşılaştırma yapılmadıkça, referans karşılaştırması kullanılmadıkça herhangi bir anlam ifade etmez. dır-dir. Bu konunun adının "C # (.NET) Tasarım Kusurları" olduğunu unutmayın.
dalle

12

Beni rahatsız eden şeylerden biri Predicate<T> != Func<T, bool>paradoks. İkisi de türden delege T -> boolama yine de atama uyumlu değiller.


Delegate.Create ve dönüşümü yapmak için bazı cast kullanmanın bir numarası var, ancak en azından açık bir cast yapabilmek güzel olurdu (ancak örtük destek eksikliğini anlayabiliyorum)
Guvante

Genel olarak delegelerin tasarımı kusurludur; örneğin zayıf olayların olmaması (abonenin özel çabası olmadan kaynak tarafındaki zayıf olayların uygulanması yalnızca bir grup yansıma ve ReflectionPermission ile yapılabilir, bkz. codeproject.com/Articles/29922/Weak-Events-in-C ), ve delegelerin referans türleri olması gerekliliğinden kaynaklanan verimsizlik (delegeler daha hızlı olurdu ve çoğu durumda değer türleri olsaydı 1/3 oranında bellek kullanırlardı - o zaman basitçe bir çift
desteden

11

Bazı insanlar (ISV'ler) dotNet çalışma zamanına ihtiyaç duymayan yerel bir yürütülebilir dosya oluşturmak için derleme zamanında makine koduna derleyebilmenizi ve onu bağlamanızı ister.


Çalıştırmadan önce programınızı NGEN yapabilmelisiniz.
Otávio Décio

Çalışma zamanının bağımlılıklarını kaldırmakla aynı şey değildir. Yalnızca kodunuz üzerinden çalıştırılan ilk JIT'i kaydedersiniz.
Ed S.


Bunu yapan bir gizleme aracı yok mu? Çerçeveyi exe'nize yerleştirir, böylece onu dağıtmanız gerekmez. İsmini hatırlayamıyorum ... PreEmptive'a ait değil ...
JoshBerke

2
Xenocode'un Postbuild'i bunu yapmıyor mu? Visual Studio'nun bunu yapmanın bir yolu olsaydı güzel olurdu ...
BenAlabaster

11

Doğru OO teknikleri hakkında çok şey biliyoruz . Ayrıştırma, sözleşmeyle programlama, uygunsuz kalıtımdan kaçınma, istisnaların uygun kullanımı, açık / kapalı yönetici, Liskov ikame edilebilirliği vb. Henüz hiçbiri, .Net çerçeveleri en iyi uygulamaları kullanmamaktadır.

Bana göre .Net tasarımındaki en büyük kusur, devlerin omuzlarında durmak değil; Çerçevelerini kullanan programcı kitlelerine ideal programlama paradigmalarından daha azını tanıtmak .

MS buna dikkat etseydi, yazılım mühendisliği dünyası bu on yılda kalite, kararlılık ve ölçeklenebilirlik açısından büyük sıçramalar yapabilirdi, ancak ne yazık ki geriliyor gibi görünüyor.


4
Hedeflenen rant için +1; Buna her sınıfın alt sınıflara girememesini, temel sınıflar için arayüzlerin eksikliğini ve birkaç yıl sonra bile çerçeve hatalarını düzeltme konusundaki isteksizliği ekleyeceğim
Steven

1
Takip etmeyi kesersek, .Net çerçevelerinin o kadar kötü olduğunu ve hangi kusurun en kötü olduğuna karar vermenin zor olduğunu mu söylüyoruz? Bir havalandırma sonrasında kendimi daha iyi hissediyorum ve MS hayranları tarafından bağırılmayı beklediğimden olumlu oyu takdir ediyorum.
Daniel Paull

Her iki şekilde de oy vermedim ve yine de olumsuz oy verme konusunda çok isteksizim, ancak neden SADECE ÖNEMSİZ OLMADIĞIMI anlamaya çalışıyorum.
Mike Dunlavey

2
Sanırım "olabileceği kadar iyi değil" diyorsunuz ki bu bir cevap değil. Hiçbir sey mükemmel değildir. Ayrıntıları sağlayın.
jcollum

3
Hayır, tasarımın açıkça kusurlu olduğu çok sayıda örnek olduğunu söylüyorum, o zaman doğru yaptıklarını düşündüğünüzde, yine de yanlış anlıyorlar. Örneğin, buradaki MSDN forumlarındaki gönderim: social.msdn.microsoft.com/forums/en-US/wpf/thread/…
Daniel Paull

11

C # switch deyimini beğenmedim.

Bunun gibi bir şey isterim

switch (a) {
  1    : do_something;
  2    : do_something_else;
  3,4  : do_something_different;
  else : do_something_weird; 
}

Böylece artık kesintiler (unutulması kolay) ve farklı değerleri virgülle ayırma imkanı yok.


Aslında, tek bir ifade ya da süslü parantez içinde bir blok gerektirmesi daha iyi olacağını düşünüyorum - C # 'teki diğer her şey gibi. Düşme yeteneği olmadan, mevcut kesme sözdizimi biraz tehlikelidir ve kapsamı da sınırlamaz. (AFAIK, olabilir, bilmiyorum)
Tamas Czinege

Ne demek istediğini anlamıyorum. Kendi 'ideal' anahtar ifadenizi buraya gönderin. Düşmek istemiyorum ama virgülle ayrılmış değerler.
tuinstoel

8
Bu arada, OP'ye katılıyorum. switchC'nin kasıtlı olarak sakatlanmış versiyonunu taklit eden tüm dillerde temelden kırılmıştır (hız için optimize edilmiştir!). VB çok daha iyi sonuç verir, ancak yine de kalıp eşleştirmeli dillerin (Haskell, F #…) ışık yılı geride kalmaktadır.
Konrad Rudolph

1
tuinostel: switch (a) {case 1 {do_something; } durum 2 {do_something_else; }} - yani, break ifadesinden kurtulmak ve her durum için uygun kod blokları gerektirmek
Tamas Czinege

2
Break olması genel olarak bir hata gibi görünüyor, onu yaymak bir derleme hatası değil mi? Görünüşe göre sadece C'den C'ye geçişi kolaylaştırmak için (diğer bir deyişle geliştiricilere otomatik olarak
düşemeyeceklerini öğretmek

10

Dinleyicileri açıkça kontrol etmeniz gereken C # olayları. Olayların amacı, orada olanlara yayınlamak değil miydi? Hiç olmasa bile?


1
İstediğiniz zaman bunun için şeker olmaması can sıkıcı, neden bunu zor yaptıklarını anlıyorum, gerekmiyorsa olayın argümanlarını başlatmamayı teşvik ediyor
ShuggyCoUk

Hm. Ya anlamıyorum ya da katılmıyorum ya da ikisi birden :-). Herhangi bir şeyi örnekleme konusunda cesaretimin kırıldığını söyleyemem. Bu bana erken optimizasyon gibi kokuyor.
Thomas Eyde

Kısmi yöntemler, bazı durumlarda geçerli bir alternatiftir.
Robert Harvey

ARTI bunun neden olduğu tüm eşleştirme ... MS, bu sorunların her ikisini de düzelten CAB'ye sahiptir, ancak CAB'nin C #'daki sınırlamalar nedeniyle kendi başına birçok sorunu vardır (örneğin, olay konuları olarak numaralandırmak yerine dizeler) - neden sadece gevşek yapmayasınız? dilin -coupled olaylar parçası !?
BlueRaja - Danny Pflughoeft

9

İç içe / yinelemeli yineleyicilerin korkunç (ve çoğu insan için oldukça görünmez) O (N ^ 2) davranışı .

Bu konuyu bildikleri, nasıl düzelteceklerini bildikleri için oldukça üzgünüm, ancak dahil olmayı hak etmek için yeterli önceliğe sahip olarak görülmüyor.

Her zaman ağaç benzeri yapılarla çalışıyorum ve aksi takdirde akıllı insanların kodunu, yanlışlıkla bu şekilde çok pahalı işlemler başlattıklarında düzeltmek zorundayım.

"Getiri foreach" in güzelliği, daha basit, daha kolay sözdiziminin doğru, performanslı kodu teşvik etmesidir. Bu, platformun uzun vadeli başarısı için yeni özellikler eklemeden önce hedeflemeleri gerektiğini düşündüğüm "başarı çukuru" dur.


Bu özellikle kusurludur, çünkü O davranışına ilişkin sezgisel beklentilere aykırıdır ve birçok insanı tuzağa düşürür
oefe

7

Bazı sınıflar arabirimler uygular ancak bu arabirimin birçok yöntemini uygulamazlar, örneğin Array IList'i uygular ancak 9 yöntemden 4'ü NotSupportedException http://msdn.microsoft.com/en-us/library/system.array_members .aspx


Bir Dizideki öğe sayısını değiştiremezsiniz, bu nedenle Add, Clear, Insert ve Remove (At) 'ın NotSupported'ı atmaktan başka yapabileceği hiçbir şey yoktur ... Aslında, IList'in true döndüren HERHANGİ bir uygulamasını beklerim IsFixedSize onlara fırlatır.
CB

@CB partiye biraz geç sanırım :) Ama Array "IList" i tatmin edemiyorsa, neden yine de uygulasın? Bu, SOLID prensibinde L'nin ihlalidir.
wingerse

7

Arabirimlerdeki statik üyeler ve iç içe geçmiş türler.

Bu, özellikle bir arayüz üyesi arayüze özgü bir parametreye ( örneğin, bir enum) sahip olduğunda faydalıdır . Numaralandırma türünü arabirim türüne yerleştirmek güzel olurdu.


1
Bu diğer önerinize çok benzemiyor mu?
RCIX

1
Hayır, bu C # dili hakkında, diğeri ise çerçeve ile ilgili. Herkes ayrımın umurunda değil, bu yüzden bunun neye izin verildiğiyle, diğeri de neyin sağlandığıyla ilgili olduğunu söyleyeyim.
Jay Bazuzi

6

Olayların son derece tehlikeli varsayılan doğası. Bir etkinliği arayabilmeniz ve kaldırılan aboneler nedeniyle tutarsız bir durumda olabilmeniz gerçekten korkunç. Bkz Jon Skeet en ve Eric Lippert en konuda daha fazla okuma için mükemmel makaleler.


Olayların varsayılan olarak iş parçacığı açısından güvenli olmaması umurumda değil (tek iş parçacıklı kodda performansı artırabilir); Aptalca olan, ekle / kaldır'ın varsayılan olarak güvenli olmasıdır, ancak bir olayı tetiklemenin doğal yolu güvensizdir ve onu kolayca güvenli hale getirecek bir tesis yoktur.
Qwertie

@Qwertie: Aptal olan şey, bir süre için ekle / kaldır'ın kilitlemeyi kullanacağı ve yine de iş parçacığı güvenli olmayacağıdır.
supercat

6
  • null her yerde.

  • const Hiçbir yerde.

  • API'ler tutarsızdır, örneğin bir dizi dönüşünü mutasyona uğratmak, voidancak bir diziye eklemek StringBufferaynı değişebilir StringBuffer.

  • Toplama arayüzleri, değişmez veri yapılarıyla uyumsuzdur, örneğin Add, System.Collections.Generic.IList<_>bir sonuç döndüremez.

  • Yapısal yazım yok, bu yüzden System.Windows.Media.Effects.SamplingMode.Bilinearsadece yazmak yerine yazarsınız Bilinear.

  • Değişken IEnumeratorbir değişmez olması gerektiği zaman arayüz sınıfları tarafından uygulanan struct.

  • Eşitlik ve karşılaştırma bir karmaşa: Elinizdeki System.IComparableve Equalsancak o zaman da var System.IComparable<_>, System.IEquatable, System.Collections.IComparer, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable, System.Collections.Generic.IComparerve System.Collections.Generic.IEqualityComparer.

  • Tuplelar yapılar olmalıdır, ancak yapılar kuyruk çağrılarının ortadan kaldırılmasını gereksiz yere engeller, böylece en yaygın ve temel veri türlerinden biri gereksiz yere ayırır ve ölçeklenebilir paralelliği bozar.


CLR'de yapısal bir yazım yoktur, ancak tür çıkarımıyla veya "semboller" olarak bilinen Ruby özelliğiyle karıştırılmış yapısal yazımınız var gibi görünüyor. Yapısal tipleme, CLR'nin Func <int, bool> ve Predicate <int> 'in aynı tip veya en azından dolaylı olarak dönüştürülebilir olduğunu düşünmesi durumunda olacaktır.
Qwertie

Karşılaştırmadan bahsetmişken, Comparer <T> 'i unutmayın!
Qwertie

@Qwertie OCaml'daki polimorfik değişkenler gibi programlama dili özelliklerine atıfta bulunuyordum. OCaml'ın LablGL kitaplığı, grafikler bağlamında yararlı olan birçok ilginç yapısal tipleme örneğine sahiptir. Tür çıkarımıyla hiçbir ilgisi yoktur ve yalnızca sembollerle teğetsel olarak ilgilidir.
JD

1
Değişmez yapı nasıl kullanılır IEnumerator?
supercat

5

0 enum olarak ayışığı

enum özellikleri: http://blogs.msdn.com/abhinaba/archive/2007/01/09/more-peculiarites-of-enum.aspx

bu iyi örnekte gösterildiği gibi: http://plus.kaist.ac.kr/~shoh/postgresql/Npgsql/apidocs/Npgsql.NpgsqlParameterCollection.Add_overload_3.html

benim önerim, "@" işaretini iyi bir şekilde kullanın:

onun yerine:

eğer ((myVar & MyEnumName.ColorRed)! = 0)

bunu kullan:

eğer ((myVar & MyEnumName.ColorRed)! = @ 0)


1
+1 numaralandırması Java'nın doğru yaptığı birkaç şeyden biri C # yapmadı
BlueRaja - Danny Pflughoeft

5

Başkaları tarafından zaten yapılmış olan uzun iyi noktalar listesine eklemek için:

  • DateTime.Now == DateTime.Now çoğu durumda, ancak her durumda değil.

  • StringDeğişmez olan, yapım ve manipülasyon için bir dizi seçeneğe sahiptir, ancak StringBuilder(değiştirilebilir olan) yoktur.

  • Monitor.Enterve Monitor.Exitörnek yöntemler olmalıydı, bu nedenle belirli bir nesneyi kilitlemek için yenileştirmek yerine, onu yenileyebilir Monitorve kilitleyebilirsiniz.

  • Yıkıcılar asla yıkıcı olarak adlandırılmamalıydı. ECMA spesifikasyonu, C ++ kalabalığı için çok daha az kafa karıştırıcı olan, bunlara sonlandırıcılar diyor, ancak dil spesifikasyonu hala onları yıkıcı olarak gösteriyor.


3
DateTime.NowBir geri kalanı için dünyanın en bariz yarış koşul, ancak 1 olduğunu
BlueRaja - Dany Pflughoeft

Bu bir yarış durumu değil, onu bir mülk haline getirmiş olmalarıdır. Özellikler tam olarak alanlar gibi görünür, bu nedenle IMO oldukça şaşırtıcı bir davranıştır.
Brian Rasmussen

4
@Brian Rasmussen: DateTime.Now, okuyarak değil, dış etkenler tarafından değiştirildiği için oldukça düzgün bir özelliktir. SomeForm.Width gibi bir özellik okursa ve kullanıcı formu yeniden boyutlandırdıktan sonra tekrar okursa, ikinci okumadaki değer farklı olacaktır. İlk DateTime.Now'un, ikincisi tarafından okunan değeri etkileyecek kadar uzun sürmesi mümkün olsa da, bu tür bir etki, yürütülmesi aynı süreyi alan herhangi bir işlevden farklı olmayacaktır.
supercat

4

Özellikleri kullanma şeklimiz bazen beni rahatsız ediyor. Bunları Java'nın getFoo () ve setFoo () yöntemlerinin eşdeğeri olarak düşünmeyi seviyorum. Ama değiller.

Eğer Mülkiyet Kullanım Yönergeleri seri çalışabilir böylece özellikler gerekir devlet, herhangi bir sırada ayarlanması o zaman onlar sizsiniz, setter zamanlı doğrulama için yararsız. Bir nesnenin kendisinin geçersiz bir duruma geçmesine izin vermesini önlemek istediğiniz bir arka plandan geliyorsanız, özellikler sizin çözümünüz değildir. Bazen bu yüzden biz şeyin türden şeyler sınırlı yetkiye beri, kamu üyelerine daha iyi ne kadar görmek için başarısız gerekiyordu .

Bu amaçla, mülkiyet sözdizimini bir şekilde genişletebilmeyi her zaman diledim (bu çoğunlukla burada yüksek sesle düşünmektir, keşke böyle bir şey yapabilseydim). Bunun gibi bir şey hayal edin:


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;
    }
}

Bunun yararlı olup olmadığından veya bunlara erişmenin nasıl görüneceğinden emin değilim. Ama keşke özelliklerle daha fazlasını yapabilmeyi ve onlara gerçekten get ve set yöntemleri gibi davranabilmeyi diliyorum.


3
Bunun gibi şeyler yapmak için ISerializable arayüzü ve örtük kurucu sağladıklarına inanıyorum, diğer bir deyişle serileştiricinin sadece özellikleri çağırmasını istemediğinizde. Biraz daha fazla çalışma olsa da, bu işin çoğunu zaten yönteminizle yapıyormuşsunuz gibi görünüyor.
Guvante

4

Genişletme yöntemleri güzeldir, ancak mixins konusunda gerçek karışımlarla daha temiz bir şekilde çözülebilecek sorunları çözmenin çirkin bir yoludur (neden bahsettiğimi görmek için Ruby'ye bakın). Bunları dile eklemenin gerçekten güzel bir yolu, jeneriklerin kalıtım için kullanılmasına izin vermek olurdu. Bu, mevcut sınıfları güzel bir nesne yönelimli şekilde genişletmenize olanak tanır:

public class MyMixin<T> : T
{
    // etc...
}

bu, örneğin bir dizeyi genişletmek için şu şekilde kullanılabilir:

var newMixin = new MyMixin<string>();

Uzatma yöntemlerinden çok daha güçlüdür çünkü yöntemleri geçersiz kılmanıza izin verir, örneğin bunları dil içinde AOP benzeri işlevselliğe izin verecek şekilde sarmalamak için.

Söz için özür dilerim :-)


5
İlginç ama ben uzatma yöntemlerini tercih ediyorum. Dizeler için bir dizi uzantı yöntemi içeren bir kitaplık alırsam, yeni şeyleri almak için tüm dize referanslarını MyMixin <string> olarak değiştirmek zorunda kalmak istemiyorum. Bu biraz küçük, elbette, ancak yöntemlerin şeffaf eklenmesi, uzatma yöntemlerini bu kadar güzel yapan şeydir.
RCIX

BTW bunun zaten çalıştığını biliyor muydunuz?
RCIX

2
LINQ bu şekilde işe yarayabilir nasıl görmüyorum
Danny Pflughoeft - BlueRaja

2
@RCIX: Mixins, uzantı yöntemlerinin çalışması gerektiğini düşündüğüm gibi geliyor. Uzantı yöntemlerini örtük yapmanın sorunu, gerçek sınıf üyelerinin genişletme yöntemlerine göre önceliğe sahip olması gerektiği anlamına gelir. Bir uzantı yöntemi Graphics.DrawParallelogram (Kalem p, Nokta v1, Nokta v2, Nokta v3) tanımlanırsa ve daha sonra, noktaları farklı bir sırada kullanan System.Graphics'e bir DrawParallelogram işlevi eklenirse, uzantı yöntemini kullanan kod, uyarı. BTW, uzatma yöntemleri için iki nokta kullanıldığında herhangi bir sorun olur muydu (örneğin, object..method ()?)
supercat

3

Microsoft, çerçevedeki bariz hataları düzeltmeyecek ve son kullanıcıların bunları düzeltebilmesi için kancalar sağlamayacaktır.

Ayrıca, çalıştırılabilir .NET çalıştırılabilir dosyalarını çalışma zamanında ikili yama yapmanın bir yolu yoktur ve yerel kitaplıklara ikili yama uygulamadan (yükleme çağrısını kesmek için) .NET çerçeve kitaplıklarının özel sürümlerini belirtmenin bir yolu yoktur ve ILDASM yeniden dağıtılamaz, bu yüzden otomatikleştiremiyorum yine de yama.


1
Hangi bariz çerçeve hatalarını kastediyorsunuz?
Robert Rossney

1
# 1 Kaydırılabilir bir kontrolün kısmen görünür bir alt kontrolüne tıklayın. Denetim, MouseDown olayını almadan önce görünüme kaydırılarak tıklamanın denetimde beklenenden başka bir yerde olmasına neden olur. Sürükleme işlemini de tetiklediği ağaç görünümlerinde daha kötüdür.
Joshua


3
  • Boş değişken üzerinde bir uzantı yöntemini çağırabilmek tartışılabilir örn.

    nesne a = boş; a.MyExtMethod (); // bu çağrılabilir, bir yerde MyExtMethod'u tanımladığını varsayın

    Kullanışlı olabilir, ancak boş referans istisna konularında belirsizdir.

  • Bir 'kusur' adlandırma. System.configuration.dll dosyasındaki "yapılandırma" nın 'C'si büyük harfle yazılmalıdır.

  • İstisna işleme. İstisna zorla yakalanmalı veya Java'da olduğu gibi fırlatılmalıdır, derleyici derleme zamanında kontrol etmelidir. Kullanıcılar, hedef çağrı içindeki istisna bilgileri için yorumlara güvenmemelidir.


3
Yine de çok kullanışlı - Parametre kontrolü için "ThrowIfNull" uzantı yöntemim var ;-p
Marc Gravell

2
Bunu yapabilirsiniz? ugh ThrowIfNull ilginç bir uzantı ama bu yanlış görünüyor.
JoshBerke

1
Düz CLR'de, boş referanslarda örnek yöntemlerini çağırabilirsiniz ve yöntem nesneye veya alanlarına erişmezse, çağrı boş referans istisnası atmaz. (Sanal olmayan yöntemler için bile callvirt kullandığından bunu C # ile yapamazsınız)
Pop Catalin

7
istisna şey tamamen yanlış. Çağrı yığınına atılabilecek her lanet olası istisnayı yakalamak ZORUNDA iseniz, hızlı başarısız olamazsınız. Ancak belirli bir çağrıda ve bunun sonucunda ortaya çıkan çağrı

3
@Will: İstisna işleme hem Java'da hem de .net'te iğrençtir, çünkü kullanılan mekanizma bir şekilde ilişkili ancak aynı zamanda biraz da ortogonal olan üç kavramı birbirine yakından bağlar: (1) Ne tür şeyler ters gitti (dizi sınır hatası, bir G / Ç zaman aşımı, vb.); (2) Sonuç olarak belirli bir kodun işlem yapıp yapmayacağı; (3) Sorunun hangi noktada "çözülmüş" olduğu düşünülmelidir. Bir nesneyi bir IEnumerable'dan okunan verilerle değiştirmesi beklenen bir rutin düşünün. Bu IEnumerable'ın işlenmesi sırasında bir istisna oluşursa ne olur?
supercat

3

Çerçevenin V1'indeki SqlCommand üzerindeki .Parameters.Add () yöntemi korkunç bir şekilde tasarlandı - 0 değerine (int) sahip bir parametreyi geçerseniz, aşırı yüklerden biri temelde çalışmazdı - bu onların yaratmasına yol açtı SqlCommand sınıfındaki .Parameters.AddWithValue () yöntemi.


Katılıyorum, ancak SqlCommand.Parameters.Add () yöntemini kastettiğinizi düşünüyorum.
Matt Peterson

3
  1. Hiçbir alt kümeleri vardır ICollection<T>ve IList<T>; en azından, bir kovaryant salt okunur koleksiyon arayüzü IListSource<out T>(bir numaralandırıcı, indeksleyici ve Sayım ile) son derece yararlı olurdu.
  2. .NET, zayıf temsilcileri desteklemez . Geçici çözümler en iyi ihtimalle beceriksizdir ve dinleyici tarafı geçici çözümler kısmi güven içinde imkansızdır (ReflectionPermission gereklidir).
  3. Genel arayüz birleştirmesi, mantıklı olsa ve hiçbir soruna neden olmasa bile yasaktır .
  4. C ++ 'dan farklı olarak, ortak değişken dönüş türlerine .NET'te izin verilmez.
  5. Eşitlik için iki değer türünü bit bazında karşılaştırmak mümkün değildir. İşlevsel " kalıcı " bir veri yapısında, birTransform(Sequence<T>, Func<T,T>) işlevin aynı değeri mi yoksa farklı bir değeri mi döndürdüğünü hızlı bir şekilde belirlemek için gereken işlevi . İşlev, argümanlarının çoğunu / tamamını değiştirmezse, çıktı dizisi giriş dizisindeki belleğin bir kısmını / tamamını paylaşabilir. Herhangi bir T tipini bit bazında karşılaştırma yeteneği olmadan, performansı muazzam derecede düşüren çok daha yavaş bir karşılaştırma kullanılmalıdır.
  6. .NET, geçici arabirimleri (Go veya Rust'ta sunulanlar gibi) yüksek performanslı bir şekilde destekleyemiyor gibi görünüyor. Bu tür arabirimler , sınıf açıkça bu arabirimi uygulamasa bile List<T>bir varsayıma IListSource<U>(burada T: U) dönüştürme yapmanıza izin verirdi . Bu işlevselliği sağlamak için en az üç farklı kitaplık vardır (bağımsız olarak yazılır) (elbette performans dezavantajlarıyla birlikte - mükemmel bir çözüm mümkün olsaydı, bunu .NET'te bir kusur olarak adlandırmak adil olmazdı).
  7. Diğer performans sorunları: IEnumerator yineleme başına iki arabirim çağrısı gerektirir. Düz yöntem işaretçileri (IntPtr boyutlu açık temsilciler) veya değer türü temsilciler (IntPtr * 2) mümkün değildir. Sabit boyutlu diziler (rastgele tip T) sınıfların içine gömülemez. Yok WeakReference<T>(kendi yazınızı kolayca yazabilirsiniz, ancak dahili olarak dökümleri kullanacaktır.)
  8. Aynı delege türlerinin uyumsuz olarak kabul edilmesi (örtük dönüştürme yok) bazı durumlarda (örn. Predicate<T>Vs Func<T,bool>) benim için rahatsızlık yarattı . Sıklıkla , bileşenler arasında daha gevşek bir bağlantı elde etmek için arabirimler ve temsilciler için yapısal tipleme yapabilmeyi diliyorum , çünkü .NET'te bağımsız DLL'lerdeki sınıfların aynı arabirimi uygulaması yeterli değildir - aynı zamanda üçüncü birine ortak bir referansı paylaşmaları gerekir Arayüzü tanımlayan DLL.
  9. DBNull.Valuenullaynı amaca eşit derecede iyi hizmet etmiş olsa bile vardır .
  10. C # 'da ?? = operatörü yoktur; yazmalısın variable = variable ?? value. Gerçekten de, C # 'da gereksiz yere simetriden yoksun olan birkaç yer vardır. Örneğin if (x) y(); else z();(parantez olmadan) yazabilirsiniz , ancak yazamazsınız try y(); finally z();.
  11. Bir evre yaratırken, alt evrenin ana evreden evre-yerel değerleri devralmasına neden olmak imkansızdır. BCL yalnızca bunu desteklemez, aynı zamanda tüm evreleri manuel olarak oluşturmadıkça kendiniz uygulayamazsınız; bir iş parçacığı oluşturma olayı olsa bile, .NET size belirli bir iş parçacığının "ebeveynlerini" veya "alt öğelerini" söyleyemez .
  12. Farklı veri türleri için "Uzunluk" ve "Sayım" olmak üzere iki farklı uzunluk özniteliğinin olması küçük bir sıkıntıdır.
  13. WPF'nin zayıf tasarımı hakkında sonsuza kadar devam edebilirim ... ve WCF (bazı senaryolar için oldukça yararlı olsa da) da siğillerle doludur. Genel olarak, BCL'nin yeni alt kitaplıklarının çoğunun şişkinliği, farkında olmaması ve sınırlı dokümantasyonu, beni bunları kullanmakta isteksiz kılıyor. Yeni şeylerin çoğu çok daha basit, daha küçük, kullanımı ve anlaşılması daha kolay, daha gevşek bir şekilde birleştirilmiş, daha iyi belgelenmiş, daha fazla kullanım senaryosuna uygulanabilir, daha hızlı ve / veya daha güçlü yazılmış olabilirdi.
  14. Genellikle özellik alıcıları ve ayarlayıcılar arasındaki gereksiz bağlantı tarafından ısırılırım: Türetilmiş bir sınıfta veya türetilmiş arabirimde, temel sınıf veya temel arabirim yalnızca bir alıcıya sahipken bir ayarlayıcı ekleyemezsiniz; bir alıcıyı geçersiz kılarsanız, bir ayarlayıcı tanımlamanıza izin verilmez; ve ayarlayıcıyı sanal, alıcıyı sanal olmayan olarak tanımlayamazsınız.

Ben bir alt kümeleri konusunda sana katılıyorum IList<T>ben kullanırım olsa IReadableByIndex<out T>ve IAppendable<in T>. Diğer şeylerin çoğu benim de aynı fikirde olduğum ciyaklamalar.
supercat

Bu gerçekten uzun bir isim. Belki uzlaşabiliriz IListReader<T>;) - "kaynak" kelimesini "havuz" un zıttı olarak kullanıyorum (salt yazılır bir arayüz).
Qwertie

Belki IListSource<in T>veya IReadableList<out T>. Temel arabirim türlerinin tüm türevlerde bulunmayan yöntemleri içermesinin değeri olabilir, ancak arabirimlerin bir şekilde özelleşmiş olmasının çoğu zaman iyi olduğunu düşünüyorum. Örneğin, IList<T>çalışabilecek veya çalışmayabilecek yeniden boyutlandırma yöntemlerini içeren IResizableList<T>ve aynı yöntemleri uygulayan, ancak bunların çalışması gerektiğini garanti eden bir tane olabilir. Böyle bir yaklaşım, bir alanın değişebilen bir listeye mevcut tek referansı veya değişmez bir listeye paylaşılan bir referansı içerdiği durumlarda faydalı olabilir.
supercat

Böyle bir durumda, listenin içeriğini değiştirmek isteyen kod, değişken bir tür olup olmadığını kontrol eder ve değilse, değişmez listeyle aynı öğeleri içeren yeni bir değiştirilebilir örnek oluşturur ve sonra onu kullanmaya başlar. Kodun, üzerinde bir değiştirme yöntemi kullanmak istediği her seferinde alanı sürekli olarak yazmak zorunda kalması rahatsız edici olurdu.
supercat

@supercat Bu sadece rahatsız edici çünkü C # bir arayüzün uygulanıp uygulanmadığını kontrol etmek ve hemen kullanmak için gerçekten kolay bir yol sağlamıyor. MS bunu kolaylaştırmak için bir dil özelliği eklemelidir. Tercih ettiğim teknik bağlayıcı bir ifade olacaktır if (rl:(list as IResizableList<T>) != null) rl.Add(...);, ancak başka öneriler de var. Çeşitli koleksiyonların ve koleksiyon adaptörlerinin yazarı olarak beni rahatsız eden şey, istisnalar atan pek çok kukla yöntem yazmak. Bir tür güvenlik hayranı olarak, yasadışı yöntemleri çağırmama izin verilmesini istemiyorum. Bir IntelliSense hayranı, bunların listelenmesini istemiyorum.
Qwertie

2

1.x'te beni işaretleyen bir şey System.Xml.XmlValidatingReader, ValidationEventHandler's' ler ValidationEventArgs, ve XmlSchemaExceptiongibi tüm yararlı bilgileri içeren temelini (dahili olarak işaretlenmiş) açığa çıkarmamasıydı . Bunun yerine, bunu Message string özelliğinden ayırmanız veya ortaya çıkarmak için yansıma kullanmanız beklenir. Son kullanıcıya daha temiz bir hata vermek istediğinizde o kadar iyi değil.linenumberposition


1

Bir numaralamanın değerlerini başka bir numaralandırmada kullanamamanız hoşunuza gitmiyor, örneğin:

    enum Colors { white, blue, green, red, black, yellow }

    enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow } 

2
Bu mantıklı bile değil. typeof(Color)! = typeof(SpecialColors).
Kirk Woll

10
enum SpecialColors { blue = Colors.blue, red = Colors.red, yellow = Colors.Yellow }
Yapması

0

Örtük olarak yazılan değişkenler kötü bir şekilde IMO olarak uygulanmıştır. Bunları gerçekten yalnızca Linq ifadeleriyle çalışırken kullanmanız gerektiğini biliyorum, ancak bunları yerel kapsamın dışında ilan edememeniz can sıkıcı.

MSDN'den:

  • var yalnızca yerel bir değişken bildirildiğinde ve aynı ifadede başlatıldığında kullanılabilir; değişken null olarak veya bir yöntem grubuna veya anonim bir işleve başlatılamaz.
  • var, sınıf kapsamındaki alanlarda kullanılamaz.
  • Var kullanılarak bildirilen değişkenler, başlatma ifadesinde kullanılamaz. Başka bir deyişle, bu ifade yasaldır: int i = (i = 20); ancak bu ifade bir derleme zamanı hatası üretir: var i = (i = 20);
  • Birden çok örtük olarak yazılmış değişken, aynı ifadede başlatılamaz.
  • Var adlı bir tür kapsam dahilindeyse, var anahtar sözcüğü bu tür adına çözümlenecek ve örtük olarak yazılan yerel değişken bildiriminin bir parçası olarak değerlendirilmeyecektir.

Bunun zayıf bir uygulama olduğunu düşünmemin nedeni, ona var dedikleri, ancak bir varyant olmaktan çok uzak. Sınıf adını tam olarak yazmak zorunda kalmamak için gerçekten sadece kısa sözdizimidir (Linq ile kullanılması dışında)


Kesinlikle anon türleri (yeni {...}), örtük olarak yazılmamış (var)
Marc Gravell

Sadece gönderdiklerimi tekrar okuyun ve yanlıştı. Örtük olarak yazılan değişkenleri
kastetmiştim

1
Eric Lippert, var neden yöntemlerin dışında kullanılamayacağını açıkladı, çünkü temelde bir kara kutu olasılık yaratıyor. blogs.msdn.com/ericlippert/archive/2009/01/26/…
Guvante

1
var değişken olarak tasarlanmamıştır! tam olarak stenografi içindir (özellikle anon türleri). geldiğinde dinamiğin tadını çıkarın ...
ShuggyCoUk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.