C # veya .NET en kötü gotcha nedir? [kapalı]


377

Son zamanlarda bir DateTimenesne ile çalışıyordum ve şöyle bir şey yazdım:

DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?

İçin intellisense dokümantasyon AddDays(), tarihe bir gün eklediğini söylüyor, ki aslında - bir gün eklenmiş bir tarih döndürüyor , bu yüzden şöyle yazmanız gerekiyor:

DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date

Bu beni daha önce birkaç kez ısırdı, bu yüzden en kötü C # gotchas katalog için yararlı olacağını düşündüm.


157
dönüş DateTime.Now.AddDays (1);
crashmstr

23
AFAIK, yerleşik değer türlerinin tümü değişmezdir, en azından türle birlikte gelen herhangi bir yöntemin, mevcut öğeyi değiştirmek yerine yeni bir öğe döndürmesi nedeniyle. En azından, bunu yapmayan kafamın üstünden birini düşünemiyorum: hepsi güzel ve tutarlı.
Joel Coehoorn

6
Değişken değer türü: System.Collections.Generics.List.Enumerator :( (Ve evet, yeterince denerseniz garip bir şekilde davrandığını görebilirsiniz.)
Jon Skeet

13
Intellisense size ihtiyacınız olan tüm bilgileri verir. Bir DateTime nesnesi döndürdüğünü söylüyor. Eğer geçtiğinizi değiştirseydiniz, geçersiz bir yöntem olurdu.
John Kraft

20
Mutlaka değil: StringBuilder.Append (...), örneğin "this" değerini döndürür. Akıcı arayüzlerde oldukça yaygındır.
Jon Skeet

Yanıtlar:


304
private int myVar;
public int MyVar
{
    get { return MyVar; }
}

Blammo. Uygulamanız yığın izlemesi olmadan kilitleniyor. Her zaman olur.

( Alıcıda MyVarküçük harf yerine sermayeye dikkat edin myVar.)


112
ve SO bu site için uygun :)
gbjbaanb

62
Özel üyeye alt çizgi koydum, çok yardımcı oluyor!
chakrit

61
Yapabileceğim otomatik özellikler kullanıyorum, bu tür problemleri çok durduruyor;)
TWith2Sugars

28
Bu özel alanlar için ön ekleri kullanmak için BÜYÜK nedenidir (başkaları da vardır, ama bu iyi biridir): _myVar, m_myVar
jrista

205
@jrista: Ey lütfen HAYIR ... değil m_ ... korkuyu aargh ...
fretje

254

Type.GetType

Bir sürü insanı ısırdığımı gördüm Type.GetType(string). Kendi montajlarındaki türler için neden işe yaradığını merak ediyorlar System.String, ancak bazıları gibi , ama değil System.Windows.Forms.Form. Cevap, sadece mevcut montajda ve içeriye bakmasıdır mscorlib.


Anonim yöntemler

C # 2.0, böyle kötü durumlara yol açan anonim yöntemler tanıttı:

using System;
using System.Threading;

class Test
{
    static void Main()
    {
        for (int i=0; i < 10; i++)
        {
            ThreadStart ts = delegate { Console.WriteLine(i); };
            new Thread(ts).Start();
        }
    }
}

Ne yazdıracak? Tamamen programlamaya bağlıdır. 10 sayı basacaktır, ancak muhtemelen 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 yazdıramaz. Sorun, itemsilci oluşturma noktasında değeri değil, yakalanan değişken olmasıdır. Bu, doğru kapsamın ekstra bir yerel değişkeni ile kolayca çözülebilir:

using System;
using System.Threading;

class Test
{
    static void Main()
    {
        for (int i=0; i < 10; i++)
        {
            int copy = i;
            ThreadStart ts = delegate { Console.WriteLine(copy); };
            new Thread(ts).Start();
        }
    }
}

Yineleyici blokların ertelenmiş yürütülmesi

Bu "zavallı adamın birim testi" geçmiyor - neden olmasın?

using System;
using System.Collections.Generic;
using System.Diagnostics;

class Test
{
    static IEnumerable<char> CapitalLetters(string input)
    {
        if (input == null)
        {
            throw new ArgumentNullException(input);
        }
        foreach (char c in input)
        {
            yield return char.ToUpper(c);
        }
    }

    static void Main()
    {
        // Test that null input is handled correctly
        try
        {
            CapitalLetters(null);
            Console.WriteLine("An exception should have been thrown!");
        }
        catch (ArgumentNullException)
        {
            // Expected
        }
    }
}

Cevap, kodun kaynağı içindeki CapitalLetterskodun, yineleyicinin MoveNext()yöntemi ilk çağrılana kadar yürütülmediğidir .

Benim bazı diğer tuhaflıklar var brainteasers sayfa .


25
Yineleyici örnek sapkın!
Jimmy

8
neden hep birlikte değil, her birini oylayabilmemiz için bunu 3 cevaba ayırmıyorsunuz?
chakrit

13
@chakrit: Geçmişe bakıldığında, bu muhtemelen iyi bir fikir olurdu, ama şimdi çok geç olduğunu düşünüyorum. Ayrıca daha fazla destek almaya çalışıyordum gibi görünebilirdi ...
Jon Skeet

19
Aslında AssemblyQualifiedName sağlarsanız Type.GetType çalışır. Type.GetType ("System.ServiceModel.EndpointNotFoundException, System.ServiceModel, Sürüm = 3.0.0.0, Kültür = nötr, PublicKeyToken = b77a5c561934e089");
chilltemp

2
@kentaromiura: Aşırı yük çözünürlüğü en türetilmiş türde başlar ve ağacı çalıştırır - ancak yalnızca baktığı tipte orijinal olarak bildirilen yöntemlere bakar . Foo (int) temel yöntemi geçersiz kılar, bu nedenle dikkate alınmaz. Foo (nesne) uygulanabilir, bu nedenle aşırı yük çözünürlüğü burada durur. Tuhaf, biliyorum.
Jon Skeet

194

Yeniden fırlatma istisnaları

Çok sayıda yeni geliştirici alan bir gotcha, yeniden atma istisna semantiği.

Birçok zaman aşağıdaki gibi kod görüyorum

catch(Exception e) 
{
   // Do stuff 
   throw e; 
}

Sorun, yığın izini silmesi ve sorunları daha zor hale getirmesidir, çünkü istisnanın nereden kaynaklandığını izleyemezsiniz.

Doğru kod, argüman içermeyen throw ifadesidir:

catch(Exception)
{
    throw;
}

Veya istisnayı başka bir pakete sarmak ve orijinal yığın izlemesini almak için iç istisnayı kullanmak:

catch(Exception e) 
{
   // Do stuff 
   throw new MySpecialException(e); 
}

Neyse ki, bunu ilk haftamda birisi tarafından öğrettim ve daha üst düzey geliştiricilerin kodunda buldum. Is: catch () {atmak; } İkinci kod pasajıyla aynı mı? catch (İstisna e) {atış; } yalnızca bir İstisna nesnesi oluşturmaz ve onu doldurmaz?
StuperUser

Sadece atmak yerine atmak (veya atmak) kullanma hatasının yanı sıra, sadece tekrar atmak için bir istisna yakalamaya değer olduğunda hangi vakaların olduğunu merak etmeliyim.
Ryan Lundy

13
@Kyralessa: birçok durum vardır: örneğin, bir işlemi geri almak istiyorsanız, arayan istisna almadan önce. Siz geri dönün ve sonra yeniden düşünün.
R. Martinho Fernandes

7
İnsanların istisnaları yakaladıkları ve yeniden düşündükleri her zaman görüyorum, çünkü tüm istisnaları yakalamaları gerektiği öğretiliyor, çünkü çağrı yığınına daha fazla yakalanacaklarını fark etmiyorlar. Beni deli ediyor.
James Westgate

5
@Kyralessa en büyük durum günlük kaydı yapmak zorunda olduğunuz zamandır. Hata yakalayın ve yeniden yakalayın ..
nawfal

194

Heisenberg İzleme Penceresi

Bu, istek üzerine yüklenen şeyler yapıyorsanız sizi kötü bir şekilde ısıtabilir:

private MyClass _myObj;
public MyClass MyObj {
  get {
    if (_myObj == null)
      _myObj = CreateMyObj(); // some other code to create my object
    return _myObj;
  }
}

Şimdi bunu kullanarak başka bir yerde kodunuz olduğunu varsayalım:

// blah
// blah
MyObj.DoStuff(); // Line 3
// blah

Şimdi CreateMyObj()yönteminizde hata ayıklamak istiyorsunuz . Bu nedenle, koda adım atmak amacıyla yukarıdaki Satır 3'e bir kesme noktası koydunuz. Sadece iyi bir ölçü için, yukarıdaki çizgiye _myObj = CreateMyObj();bir kesme noktası ve hatta CreateMyObj()kendi içine bir kesme noktası koyabilirsiniz .

Kod, Satır 3'teki kesme noktanıza çarpar. Koşullu kodu girmeyi beklersiniz, çünkü _myObjaçıkçası boştur, değil mi? Ah ... yani ... neden koşulu atladı ve doğruca return _myObj?! Farenizi _myObj'ın üzerine getirin ... ve gerçekten de bir değeri var! Bu nasıl oldu?!

Cevap, IDE'nizin bir değer almasına neden olmasıdır, çünkü bir "izleme" pencereniz açıktır - özellikle geçerli veya önceki yürütme satırıyla ilgili tüm değişkenlerin / özelliklerin değerlerini görüntüleyen "Autos" izleme penceresi. Hat 3'teki kırılma noktanıza çarptığınızda, izleme penceresi, değerini bilmek isteyeceğinize karar verdi MyObj- bu yüzden perde arkasında kesme noktalarınızı görmezden geldi, gitti veMyObj sizin için - çağrısına dahil CreateMyObj()olduğu _myObj!

Bu yüzden Heisenberg Gözetleme Penceresi diyorum - değeri etkilemeden gözlemleyemezsiniz ... :)

ANLADIM!


Düzenle - @ ChristianHayter'in yorumunun ana cevaba dahil edilmeyi hak ettiğini hissediyorum, çünkü bu sorun için etkili bir geçici çözüm gibi görünüyor. Öyleyse tembel yüklü bir mülkünüz olduğunda ...

Mülkünüzü [DebuggerBrowsable (DebuggerBrowsableState.Never)] veya [DebuggerDisplay ("<istek üzerine yüklenir>")] ile dekore edin. - Christian Hayter


10
parlak bulmak! bir programcı değilsiniz, gerçek bir hata ayıklayıcısınız.
bu. __curious_geek

26
Sadece saat penceresini değil, değişkenin üzerine de geçiyorum.
Richard Morgan

31
Mülkünüzü [DebuggerBrowsable(DebuggerBrowsableState.Never)]veya ile süsleyin [DebuggerDisplay("<loaded on demand>")].
Christian Hayter

4
Bir çerçeve sınıfı geliştiriyorsanız ve tembel olarak oluşturulmuş bir özelliğin çalışma zamanı davranışını değiştirmeden izleme penceresi işlevselliği istiyorsanız, önceden oluşturulmuşsa değeri döndürmek için bir hata ayıklayıcı türü proxy kullanabilirsiniz ve özelliğin durum böyleyse inşa edilmiştir. Lazy<T>(Onun kullanım için, özellikle de sınıf Valueözelliği) bu kullanıldığı bir örnektir.
Sam Harwell

4
Aşırı yükte nesnenin değerini (bazı nedenlerden ötürü anlayamıyorum) değiştiren birini hatırlıyorum ToString. Üzerine geldiğinde araç ipucu ona farklı bir değer verdi - anlayamadı ...
JNF

144

İşte beni alan başka bir zaman:

static void PrintHowLong(DateTime a, DateTime b)
{
    TimeSpan span = a - b;
    Console.WriteLine(span.Seconds);        // WRONG!
    Console.WriteLine(span.TotalSeconds);   // RIGHT!
}

TimeSpan.Seconds , zaman aralığının saniye bölümüdür (2 dakika ve 0 saniye, 0 saniye değerine sahiptir).

TimeSpan.TotalSeconds , saniye cinsinden ölçülen tüm zaman aralığıdır (2 dakika toplam saniye değerine 120 sahiptir).


1
Evet, o da beni yakaladı. Bence ne temsil ettiğini daha açık hale getirmek için TimeSpan.SecondsPart ya da bir şey olmalı.
Dan Diplo

3
Yeniden okuma bu, ben neden merak zorunda TimeSpanbile vardır bir Secondsbütün mülk. Kim bir sıçanın kıçına bir zaman aralığının saniye kısmının ne olduğunu verir? Bu keyfi, birime bağlı bir değerdir; Bunun için pratik bir kullanım düşünemiyorum.
MusiGenesis

2
Bana TimeSpan.TotalSeconds döneceği ... zaman aralığı toplam saniye sayısı dönecek anlamlıdır.
Ed

16
@MusiGenesis özelliği yararlıdır. Zaman aralığını parçalara ayrılmış olarak görüntülemek istersem ne olur? Örneğin, Timespan'ınızın '3 saat 15 dakika 10 saniye' süresini temsil ettiğini varsayalım. Bu bilgilere Saniye, Saat, Dakika özelliği olmadan nasıl erişebilirsiniz?
SolutionYogi

1
Benzer API'ler, ben kullandım SecondsPartve SecondsTotalikisini ayırt etmek.
BlueRaja - Danny Pflughoeft

80

Hafıza sızıyor çünkü olayları açmadınız.

Bu bile biliyorum bazı üst düzey geliştiriciler yakaladı.

İçinde birçok şey bulunan bir WPF formu düşünün ve orada bir yerde bir etkinliğe abone olun. Aboneliği iptal etmezseniz, form kapatıldıktan ve referansı kaldırıldıktan sonra tüm form bellekte tutulur.

Gördüğüm sorun WPF formunda bir DispatchTimer oluşturmak ve Tick olayına abone oldu, zamanlayıcı üzerinde bir - = yapmazsanız formunuz bellek sızdırıyor inanıyorum!

Bu örnekte, ayırma kodunuz

timer.Tick -= TimerTickEventHandler;

WPF formunda DispatchTimer örneğini oluşturduğunuz için bu özellikle zordur, bu nedenle bunun Çöp Toplama işlemi tarafından işlenen dahili bir başvuru olacağını düşünürsünüz ... maalesef DispatchTimer statik bir dahili abonelik listesi ve hizmetleri listesi kullanır. UI iş parçacığı istekleri, bu nedenle başvuru statik sınıfa 'ait'.


1
İşin püf noktası, oluşturduğunuz tüm etkinlik aboneliklerini her zaman serbest bırakmaktır. Forms'un sizin için yapmasına güvenmeye başlarsanız, alışkanlığa gireceğinizden emin olabilirsiniz ve bir gün, yapılması gereken bir yerde bir etkinlik yayınlamayı unutabilirsiniz.
Jason Williams

3
Bence, bu sorunu çözecek zayıf referans olayları için bir MS-bağlantı önerisi var , ancak bence inanılmaz derecede zayıf olay modelini, CAB tarafından kullanılan gibi zayıf bağlanmış bir modelle tamamen değiştirmeliyiz.
BlueRaja - Danny Pflughoeft

Benden +1, teşekkürler! Peki, kod inceleme çalışmaları için hayır teşekkürler yapmak zorundayım!
Bob Denny

@ BlueRaja-DannyPflughoeft Zayıf olaylarla başka bir şansınız var - lambdalara abone olamazsınız. Sen yazamaztimer.Tick += (s, e,) => { Console.WriteLine(s); }
Ark-kun

@ Ark-kun evet lambdalar daha da zorlaştırır, lambda'nızı bir değişkene kaydetmeniz ve bunu sökme kodunuzda kullanmanız gerekir. Lamda yazmanın basitliğini yok ediyor değil mi?
Timothy Walters

63

Belki gerçekten bir gotcha değil çünkü davranış MSDN'de açıkça yazılmıştır, ancak oldukça sezgisel bulduğum için boynumu bir kez kırmıştır:

Image image = System.Drawing.Image.FromFile("nice.pic");

Bu adam "nice.pic"görüntü imha edilene kadar dosyayı kilitli bırakır . Karşı karşıya geldiğimde, simgeleri anında yüklemek güzel olurdu ve (ilk başta) düzinelerce açık ve kilitli dosya ile sonuçlandığımı fark etmedim! Görüntü dosyayı yüklediği yeri izler ...

Bunu nasıl çözebilirim? Bir astarın işi yapacağını düşündüm. Ben ekstra bir parametre bekleniyor FromFile(), ama hiçbiri yoktu, bu yüzden bu yazdı ...

using (Stream fs = new FileStream("nice.pic", FileMode.Open, FileAccess.Read))
{
    image = System.Drawing.Image.FromStream(fs);
}

10
Bu davranışın bir anlamı olmadığını kabul ediyorum. "Bu davranış tasarım gereğidir" dışında herhangi bir açıklama bulamıyorum.
MusiGenesis

1
Oh ve bu geçici çözümün en güzel yanı, Image.ToStream (tam adı elden unuttum) 'u çağırmaya çalışmanızdır.
Joshua

55
bazı kodu kontrol etmeniz gerekiyor. Brb.
Esben Skov Pedersen

7
@EsbenSkovPedersen Böyle basit ama komik ve kuru bir yorum. Günümü şenlendirdin.
Inisheer

51

ASP.NET'i sayarsanız, web formlarının yaşam döngüsünün benim için oldukça büyük bir sorun olduğunu söyleyebilirim. Birçok geliştirici sadece hangi olay işleyiciyi (ne yazık ki dahil) ne zaman kullanacağımı gerçekten anlamadığından, kötü yazılmış webformları kodunda hata ayıklamak için sayısız saat geçirdim.


26
Bu yüzden
MVC'ye geçtim

29
Özellikle ASP.NET gotchas'a adanmış başka bir soru daha vardı (haklı olarak). ASP.NET'in temel konsepti (web uygulamalarının geliştirici için Windows uygulamaları gibi görünmesi) o kadar korkunç bir şekilde yanlış yönlendirilmiş ki, bir "gotcha" olarak sayıldığından emin değilim.
MusiGenesis

1
MusiGenesis Keşke yüzlerce kez oy verebilseydim.
csauve

3
@MusiGenesis Şimdi yanlış yönlendirilmiş görünüyor, ancak o zaman insanlar web uygulamalarının (uygulamalar anahtar kelime olan ASP.NET WebForms gerçekten bir blog barındırmak için tasarlanmamıştı) Windows uygulamalarıyla aynı şekilde davranmasını istiyordu. Bu sadece nispeten yakın bir zamanda değişti ve birçok insan hala "tam olarak orada değil". Bütün sorun soyutlama yol çok sızan olmasıydı - web bir masaüstü uygulaması gibi hareket etmedi o kadar neredeyse herkes karışıklığa yol açar.
Luaan

1
İronik bir şekilde, ASP.NET hakkında gördüğüm ilk şey, Microsoft'tan ASP.NET kullanarak bir blog sitesi nasıl oluşturabileceğinizi gösteren bir video oldu!
MusiGenesis

51

aşırı yüklenmiş == operatörler ve türetilmemiş konteynerler (arraylistler, veri setleri vb.):

string my = "my ";
Debug.Assert(my+"string" == "my string"); //true

var a = new ArrayList();
a.Add(my+"string");
a.Add("my string");

// uses ==(object) instead of ==(string)
Debug.Assert(a[1] == "my string"); // true, due to interning magic
Debug.Assert(a[0] == "my string"); // false

Çözümler?

  • string.Equals(a, b)dize türlerini karşılaştırırken her zaman kullan

  • List<string>her iki işlenenin de dize olmasını sağlamak için generics kullanmak .


6
İçinde her şeyi yanlış yapan fazladan alan var - ancak boşlukları çıkarırsanız, "+" dizgim "hala sabit olduğu için son satır hala doğru olacaktır.
Jon Skeet

1
ack! haklısın :) tamam, biraz düzenledim.
Jimmy

bu tür kullanımlar için bir uyarı verilir.
chakrit

11
Evet, C # dilinin en büyük kusurlarından biri Object sınıfındaki == işleci. Bizi ReferenceEquals kullanmaya zorlamalılardı.
erikkallen

2
Neyse ki, 2.0'dan beri jeneriklerimiz var. Yukarıdaki örnekte ArrayList yerine <string> Listesini kullanıp kullanmadığınız konusunda endişelenmenize gerek yoktur. Artı biz ondan performans kazandık, yay! Her zaman eski kodumuzdaki ArrayLists'e eski referansları köklendiriyorum.
JoelC

48
[Serializable]
class Hello
{
    readonly object accountsLock = new object();
}

//Do stuff to deserialize Hello with BinaryFormatter
//and now... accountsLock == null ;)

Hikayenin ahlakı: Bir nesnenin serisini kaldırırken alan başlatıcıları çalıştırılmaz


8
Evet, varsayılan oluşturucuyu çalıştırmadığı için .NET serileştirmesinden nefret ediyorum. Keşke herhangi bir kurucu çağırmadan bir nesne inşa etmek imkansız olsaydı, ama ne yazık ki değil.
Roman Starkov

45

DateTime.ToString ("gg / aa / yyyy") ; Bu aslında edecek değil her zaman / gg AA / yyyy ama bunun yerine nerede olduğunu bağlı olarak tarih ayırıcı dikkate bölgesel ayarları alıp yerini alacak verir. Böylece dd-AA-yyyy ya da benzer bir şey elde edebilirsiniz.

Bunu yapmanın doğru yolu DateTime.ToString ("dd '/' MM '/' yyyy") kullanmaktır;


DateTime.ToString ("r") , GMT kullanan RFC1123'e dönüştürülmelidir. GMT, UTC'den bir saniyeden daha kısa bir süredir , ancak söz konusu DateTime öğesi Yerel olarak belirtilse bile , "r" biçim belirleyicisi UTC'ye dönüştürülmez .

Bu, aşağıdaki sorcha ile sonuçlanır (yerel saatinizin UTC'den ne kadar olduğuna bağlı olarak değişir):

DateTime.Parse("Tue, 06 Sep 2011 16:35:12 GMT").ToString("r")
>              "Tue, 06 Sep 2011 17:35:12 GMT"

Tüh!


19
Mm olarak mm - mm olarak değiştirildi dakika ve mm ile ay. Başka bir yakaladım sanırım ...
Kobi

1
Eğer bilmiyorsan bunun nasıl bir gotcha olacağını görebiliyordum (bilmiyordum) ... ama özellikle bir tarih yazdırmaya çalıştığın davranışı ne zaman istediğini anlamaya çalışıyorum bölgesel ayarlarınızla eşleşmiyor.
Beska

6
@Beska: Bir dosyaya yazdığınız için, bunun belirli bir tarih biçiminde, belirli bir tarih biçiminde olması gerekir.
GvS

11
Yerelleştirilen varsayılanların diğer yoldan daha kötü olduğu görüşündeyim. En azından geliştirici yerelleştirmeyi tamamen görmezden geldi, kod farklı yerelleştirilen makinelerde çalışıyor . Bu şekilde, kod muhtemelen çalışmaz.
Joshua

32
Aslında bunu yapmanın doğru yolunun olduğuna inanıyorumDateTime.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
BlueRaja - Danny Pflughoeft

44

Bunu geçen gün yayınladığını gördüm ve bence bilmeyenler için oldukça karanlık ve acı verici

int x = 0;
x = x++;
return x;

Bu çoğu beklendiği gibi 0 değil 1 döndürecektir


37
Umarım aslında insanları ısırmazdı - umarım ilk etapta yazmazlar! (Zaten ilginç, elbette.)
Jon Skeet

12
Bunun çok belirsiz olduğunu sanmıyorum ...
Chris Marasti-Georg

10
En azından, C # 'da, sonuçlar beklenmedikse tanımlanır. C ++ 'da, 0 veya 1 veya program sonlandırması dahil başka bir sonuç olabilir!
James Curran

7
Bu bir sorun değil; x = x ++ -> x = x, sonra x değerini artırın x x x ++ x -> x değerini artırıp x = x
Kevin

28
@Kevin: Bunun o kadar basit olduğunu sanmıyorum. X = x ++, x = x ve ardından x ++ ile eşdeğerse, sonuç x = 1 olur. Bunun yerine, bence önce eşittir işaretinin sağındaki ifade değerlendirilir (0 vererek), sonra x artırılır (x = 1 verilir) ve son olarak atama gerçekleştirilir (bir kez daha x = 0 vererek).
Tim Goodman

39

Bu partiye biraz geç kaldım, ama son zamanlarda beni ısırmış iki yakaladım:

DateTime çözünürlüğü

Ticks özelliği, saniyenin 10 milyonda birini (100 nanosaniye blok) ölçer, ancak çözünürlük 100 nanosaniye değildir, yaklaşık 15 ms'dir.

Bu kod:

long now = DateTime.Now.Ticks;
for (int i = 0; i < 10; i++)
{
    System.Threading.Thread.Sleep(1);
    Console.WriteLine(DateTime.Now.Ticks - now);
}

size bir çıktı verecektir (örneğin):

0
0
0
0
0
0
0
156254
156254
156254

Benzer şekilde, DateTime.Now.Millisecond'a bakarsanız, 15.625 ms'lik yuvarlak parçalar halinde değerler alırsınız: 15, 31, 46, vb.

Bu özel davranış sistemden sisteme değişiklik gösterir , ancak bu tarih / saat API'sında çözümle ilgili başka öğeler de vardır.


Path.Combine

Dosya yollarını birleştirmenin harika bir yolu, ancak her zaman beklediğiniz gibi davranmaz.

İkinci parametre bir \karakterle başlarsa , size tam bir yol vermez:

Bu kod:

string prefix1 = "C:\\MyFolder\\MySubFolder";
string prefix2 = "C:\\MyFolder\\MySubFolder\\";
string suffix1 = "log\\";
string suffix2 = "\\log\\";

Console.WriteLine(Path.Combine(prefix1, suffix1));
Console.WriteLine(Path.Combine(prefix1, suffix2));
Console.WriteLine(Path.Combine(prefix2, suffix1));
Console.WriteLine(Path.Combine(prefix2, suffix2));

Bu çıktıyı size verir:

C:\MyFolder\MySubFolder\log\
\log\
C:\MyFolder\MySubFolder\log\
\log\

17
~ 15 ms'lik aralıklarla zamanın nicelendirilmesi, altta yatan zamanlama mekanizmasındaki doğruluk eksikliğinden kaynaklanmamaktadır (bu konu hakkında daha önce ayrıntılara girmeyi ihmal ettim). Bunun nedeni, uygulamanızın çok görevli bir işletim sisteminin içinde çalışmasıdır. Windows, uygulamanızı her 15ms'de bir kontrol eder ve aldığı küçük zaman diliminde, uygulamanız son diliminizden beri sıraya alınmış olan tüm iletileri işler. Bu dilimdeki tüm çağrılarınız aynı anda geri döner, çünkü hepsi aynı anda etkili bir şekilde yapılır.
MusiGenesis

2
@MusiGenesis: Nasıl çalıştığını biliyorum (şimdi), ama gerçekten o kadar kesin olmayan böyle hassas bir ölçüye sahip olmak benim için yanıltıcı görünüyor. Gerçekten en yakın on milyona yuvarlarken nanometrelerdeki boyumu bildiğimi söylemek gibi.
Damovisa

7
DateTime tek bir keneyi saklayabiliyor; DateTime.Şimdi bu doğruluğu kullanmıyor.
Ruben

16
Ekstra '\' birçok unix / mac / linux millet için bir gotcha olduğunu. Windows'da, önde gelen bir '\' varsa, bu, sürücünün köküne gitmek istediğimiz anlamına gelir (yani C :) CDne demek istediğimi görmek için bir komutta deneyin .... 1) Git C:\Windows\System322) Tip CD \Users3) Woah! Şimdi konum C:\Users... GOT IT? ... Path.Combine (@ "C: \ Windows \ System32", @ "\ Users") \Userstam olarak şu anlamına gelir[current_drive_here]:\Users
chakrit

8
'Uyku' olmadan bile bu aynı şekilde gerçekleşir. Bunun, uygulamanın her 15 ms'de bir planlanmasıyla ilgisi yoktur. DateTime.UtcNow, GetSystemTimeAsFileTime tarafından çağrılan yerel işlevin çözünürlüğü zayıf görünüyor.
Jimbo

38

Konsola yazılan bir işlemi başlattığınızda (System.Diagnostics kullanarak), ancak Console.Out akışını asla okumazsanız, belirli bir çıktıdan sonra uygulamanız askıda görünecektir.


3
Hem stdout hem de stderr'i yeniden yönlendirip sırayla iki ReadToEnd çağrısı kullandığınızda da aynı şey olabilir. Hem stdout hem de stderr'in güvenli kullanımı için, her biri için bir okuma dizisi oluşturmanız gerekir.
Sebastiaan M

34

Linq-To-Sql'de operatör kısayolu yok

Buraya bakın .

Kısacası, bir Linq-To-Sql sorgusunun koşullu yan tümcesinde, boş başvuru istisnalarını önlemek için ||ve gibi koşullu kısayolları kullanamazsınız &&; Linq-To-Sql, birinci koşul ikinci koşulu değerlendirme ihtiyacını ortadan kaldırsa bile OR veya AND operatörünün her iki tarafını da değerlendirir!


8
TIL. BRB, birkaç yüz LINQ sorgusunu yeniden optimize ediyor ...
tsilb

30

Varsayılan parametreleri sanal yöntemlerle kullanma

abstract class Base
{
    public virtual void foo(string s = "base") { Console.WriteLine("base " + s); }
}

class Derived : Base
{
    public override void foo(string s = "derived") { Console.WriteLine("derived " + s); }
}

...

Base b = new Derived();
b.foo();

Çıktı:
türetilmiş taban


10
Tuhaf, bunun tamamen açık olduğunu düşündüm. Bildirilen tür ise Base, derleyici varsayılan değeri nereden almalıdır Base? Bildirilen tür türetilmiş türü, (statik) olarak adlandırılan yöntem temel yöntem olsa bile , varsayılan değer farklı olabilir biraz daha gotcha düşündüm .
Timwi

1
Neden bir yöntemin uygulanması başka bir uygulamanın varsayılan değerini alır?
Ocak'ta staafl

1
@staafl Varsayılan bağımsız değişkenler çalışma zamanında değil, derleme zamanında çözümlenir.
fredoverflow

1
Bu gotcha'nın genel olarak varsayılan parametreler olduğunu söyleyebilirim - insanlar genellikle çalışma zamanı yerine derleme zamanında çözüldüklerini fark etmezler.
Luaan

4
@FredOverflow, sorum kavramsaldı. Her ne kadar davranış uygulama için mantıklı olsa da, sezgisel değildir ve muhtemelen bir hata kaynağıdır. IMHO C # derleyicisi, geçersiz kılma sırasında varsayılan parametre değerlerinin değiştirilmesine izin vermemelidir.
Mart'ta staafl

27

Değişken koleksiyonlardaki değer nesneleri

struct Point { ... }
List<Point> mypoints = ...;

mypoints[i].x = 10;

etkisi yoktur.

mypoints[i]Pointdeğer nesnesinin bir kopyasını döndürür . C # mutlu bir şekilde bir kopya alanı değiştirmenizi sağlar. Sessizce hiçbir şey yapmıyor.


Güncelleme: Bu C # 3.0 sabit gibi görünüyor:

Cannot modify the return value of 'System.Collections.Generic.List<Foo>.this[int]' because it is not a variable

6
Gerçekten dizilerle (cevabınızın aksine) çalıştığını düşünerek neden kafa karıştırıcı olduğunu görebiliyorum, ancak List <Point> gibi diğer dinamik koleksiyonlarla değil.
Lasse V. Karlsen

2
Haklısın. Teşekkürler. Cevabımı düzelttim :). arr[i].attr=. Eğer kütüphane kaplarda değil kod ki diziler için özel sözdizimi (Neden (<değer ifadesi>) attr = <expr> hiç izin hiç mantıklı Can.??
Bjarke Ebert

1
@Bjarke Ebert: Mantıklı olacağı bazı durumlar var, ancak maalesef derleyicinin bunları tanımlaması ve bunlara izin vermesi mümkün değil. Örnek kullanım senaryosu: "döndürme / çevirme" göstergesi ile birlikte iki boyutlu kare bir diziye referans veren değişmez bir Yapı. Yapının kendisi değişmez olacaktır, bu nedenle salt okunur bir örneğin bir öğesine yazmak iyi olmalıdır, ancak derleyici özellik ayarlayıcının aslında yapıyı yazmayacağını bilmeyecek ve bu nedenle buna izin vermeyecektir. .
supercat

25

Belki de en kötü değil, ancak .net çerçevesinin bazı bölümleri derece kullanırken diğerleri radyan kullanıyor (ve Intellisense ile görünür öğrenmek için MSDN ziyaret etmek zorunda olan söyler asla belgelerine)

Bütün bunlar Anglebunun yerine bir sınıfa sahip olmaktan kaçınabilirdi ...


Bu benim diğer FRİKİKLERİNDEN bu daha kötü olan düşünüldüğünde bu kadar çok upvotes var şaşırdım
BlueRaja - Dany Pflughoeft

22

C / C ++ programcıları için C # 'a geçiş doğaldır. Ancak, şahsen karşılaştığım en büyük gotcha (ve aynı geçişi yapan diğerleriyle gördüm) C # 'daki sınıflar ve yapılar arasındaki farkı tam olarak anlamak değildir.

C ++ 'da, sınıflar ve yapılar aynıdır; yalnızca sınıfların varsayılan olarak özel görünürlük ve yapıların varsayılan olarak genel görünürlük olarak ayarlandığı varsayılan görünürlükte farklılık gösterir. C ++ 'da, bu sınıf tanımı

    class A
    {
    public:
        int i;
    };

işlevsel olarak bu yapı tanımına eşdeğerdir.

    struct A
    {
        int i;
    };

Ancak C # 'da, sınıflar başvuru tipleridir, yapılar ise değer tipleridir. Bu BÜYÜK (1) amacı, eşitlik test üst üste, (2) kullanmak, karar vermek, (3) performans (örneğin, boks / kutusu açma) vb farkı

Web'de ikisi arasındaki farklarla ilgili her türlü bilgi vardır (örneğin, burada ). En azından farklılıklar ve etkileri hakkında çalışma bilgisine sahip olmak için C # 'a geçiş yapan herkesi şiddetle tavsiye ederim.


13
En kötüsü, insanlar dili kullanmadan önce öğrenmek için zaman ayırmaya zahmet etmiyorlar mı?
BlueRaja - Danny Pflughoeft

3
@ BlueRaja-DannyPflughoeft Görünüşe göre benzer dillerdeki klasik gotcha gibi - çok benzer anahtar kelimeler ve birçok durumda sözdizimi kullanıyorlar, ancak çok farklı bir şekilde çalışıyorlar.
Luaan

19

Çöp toplama ve atma (). Belleği boşaltmak için hiçbir şey yapmanıza gerek olmasa da , hala Dispose () aracılığıyla kaynakları boşaltmanız gerekir . Bu, WinForms'u kullanırken veya herhangi bir şekilde nesneleri izlerken unutulması son derece kolay bir şeydir.


2
Using () bloğu bu sorunu düzgün bir şekilde çözer. Dispose çağrısını her gördüğünüzde, () öğesini kullanarak hemen ve güvenle yeniden görüntüleyebilirsiniz.
Jeremy Frey

5
Endişenin ID'leri doğru bir şekilde uygulamak olduğunu düşünüyorum .
Mark Brackett

4
Öte yandan, () alışkanlığı, PInvoke ile çalışırken olduğu gibi sizi beklenmedik bir şekilde ısıtabilir. API'nın hala referansta bulunduğu bir şeyi atmak istemezsiniz.
MusiGenesis

3
IDisposable'ı doğru bir şekilde uygulamak çok zordur ve bu konuda bulduğum en iyi tavsiyeyi bile (.NET Framework Yönergeleri) sonunda "elde edene" kadar uygulamak kafa karıştırıcı olabilir.
09:46

1
IDisposable'da şimdiye kadar bulduğum en iyi tavsiye, üç kolay kural ve IDisposable hakkında derinlemesine bir makale
Roman Starkov

19

Diziler uygulamak IList

Ama bunu uygulama. Add'i çağırdığınızda, size çalışmadığını söyler. Öyleyse sınıf neden bir arabirimi destekleyemediğinde uygular?

Derler, ancak çalışmaz:

IList<int> myList = new int[] { 1, 2, 4 };
myList.Add(5);

Bu sorun çok fazla, çünkü serileştirici (WCF) tüm IListleri dizilere dönüştürüyor ve çalışma zamanı hataları alıyoruz.


8
IMHO, sorun, Microsoft koleksiyonları için tanımlanmış yeterli arabirim yok olmasıdır. IMHO, iEnumerable, iMultipassEnumerable (Sıfırlamayı destekler ve birden fazla geçişin eşleşeceğini garanti eder), iLiveEnumerable (numaralandırma sırasında koleksiyon değişirse kısmen tanımlanmış semantiklere sahip olmalıdır - numaralandırmada değişiklikler görünebilir veya görünmeyebilir, ancak neden olmamalıdır sahte sonuçlar veya istisnalar), iReadIndexable, iReadWriteIndexable, vb. Arabirimler diğer arabirimleri "devralabilir" olduğundan, bu çok fazla iş ekleyemezdi, eğer varsa (NotImplemented saplamaları kaydedecekti).
supercat

@supercat, yeni başlayanlar ve bazı uzun süreli kodlayıcılar için kafa karıştırıcı olurdu. Bence .NET koleksiyonları ve arayüzleri son derece zarif. Ama alçakgönüllülüğünü takdir ediyorum. ;)
Ürdün

@Jordan: Yukarıda yazılı beri, iyi bir yaklaşım ikisine birden sahip olurdu karar verdik IEnumerable<T>ve IEnumerator<T>bir destekleyecek Featuresiyi kimin faydası bildirildi "Özellikleri" ne göre belirlenir olacağını bazı "isteğe bağlı" yöntemlerinin yanı özelliği. Ben ana noktaya rağmen, bir kod alma sağlayan sağlar IEnumerable<T>daha güçlü vaatler gereken durumlarda vardır IEnumerable<T>. Çağrı ToList, IEnumerable<T>bu gibi vaatleri yerine getiren bir şey getirecektir, ancak çoğu durumda gereksiz yere pahalı olacaktır. Ben olması gerektiğini poz verecek ...
supercat

... bir kodu alan kodun gerektiğindeIEnumerable<T> içeriğin bir kopyasını oluşturabildiği, ancak gereksiz yere yapmaktan kaçınabileceği bir yol.
supercat

Seçenekleriniz kesinlikle okunamıyor. Kodda bir IList gördüğümde, bir özellik özelliğini araştırmak yerine ne ile çalıştığımı biliyorum. Programcılar, kodun önemli bir özelliğinin sadece bilgisayarlar tarafından değil, insanlar tarafından da okunabileceğini unutmayı sever. .NET koleksiyonları ad alanı ideal değil, ancak iyi ve bazen en iyi çözümü bulmak, bir ilkeyi daha ideal bir şekilde uydurma meselesi değildir. Her çalıştığım en kötü kod bazıları DRY ideal sığmaya çalıştı kod oldu. Onu kazıdım ve yeniden yazdım. Sadece kötü bir koddu. Çerçevenizi hiç kullanmak istemem.
Ürdün

18

foreach döngü değişkenleri kapsamı!

var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
    l.Add(() => s);
}

foreach (var a in l)
    Console.WriteLine(a());

beş "amet" yazdırırken, aşağıdaki örnek iyi çalışıyor

var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
    var t = s;
    l.Add(() => t);
}

foreach (var a in l)
    Console.WriteLine(a());

11
Bu aslında Jon'un anonim yöntemlerle örneğine eşdeğerdir.
Mehrdad Afshari

3
"S" değişkeninin kapsamlandırılmış değişkenle karıştırılmasının daha kolay olduğu her yerde foreach ile daha karmaşık olduğunu kaydedin. Ortak for-loop'ları ile indeks değişkeni her yineleme için açıkça aynıdır.
Mikko Rantanen

2
blogs.msdn.com/ericlippert/archive/2009/11/12/… ve evet, değişkenin "doğru bir şekilde" kapsamlandırılmasını diliyorum.
Roman Starkov


Aslında aynı değişkeni değiştirmeden tekrar tekrar yazdırıyorsunuz.
Ürdün

18

MS SQL Server, 1753'ten önceki tarihleri ​​işleyemez DateTime.MinDate. Dolayısıyla, bir mindatı, hatalı biçimlendirilmiş bir tarihi (yakın zamanda veri aktarımında başıma geldi) veya sadece Fatih William'ın doğum tarihini kaydetmeye çalışırsanız, başınız belada olacak. Bunun için yerleşik bir çözüm yoktur; 1753'ten önceki tarihlerle çalışmanız gerekiyorsa, kendi geçici çözümünüzü yazmanız gerekir.


17
Açıkçası MS SQL Server'ın bu hakkı olduğunu ve .Net yanlış olduğunu düşünüyorum. Araştırmayı yaparsanız, 1751'den önceki tarihlerin takvim değişiklikleri, tamamen atlanan günler, vb. Nedeniyle korkaklaştığını bilirsiniz. Çoğu RDBM'nin bazı kesme noktaları vardır. Bu bir başlangıç noktası vermelidir: ancestry.com/learn/library/article.aspx?article=3358
NotMe

11
Ayrıca, tarih 1753'tür .. Tarihler atlanmadan sürekli bir takvimimiz var. SQL 2008, 1/1/01 - 12/31/9999 tarihleri ​​kabul edebilen Date ve datetime2 veri türünü tanıttı. Ancak, 1753 öncesi tarihleri ​​gerçekten karşılaştırıyorsanız, bu türleri kullanan tarih karşılaştırmaları şüphe ile görülmelidir.
NotMe

Oh, doğru, 1753, düzeltildi, teşekkürler.
Shaul Behr

Bu tür tarihlerle tarih karşılaştırmaları yapmak gerçekten anlamlı mı? Yani, Tarih Kanalı için bu çok mantıklı, ama kendimi Amerika'nın keşfedildiği haftanın kesin gününü bilmek istediğimi görmüyorum.
Camilo Martin

5
Julian Günü'nde Wikipedia aracılığıyla, sıçrama günlerini ve 1753'te atlanan günleri göz önünde bulundurarak, 1984'te yayınlanan 13 satırlık bir temel program CALJD.BAS bulabilirsiniz. "SQL2008 gibi sistemler daha da kötüye gitmelidir. 15. yüzyılda doğru bir tarih temsiliyle ilgilenmeyebilirsiniz, ancak diğerleri yapabilir ve yazılımımız bunu hata olmadan ele almalıdır. Başka bir sorun artık saniye. . .
Roland

18

Kötü linq önbellekleme gotcha

Bkz sorumu bu keşif yol açtığını ve blogcu sorunu keşfetti.

Kısacası, DataContext, daha önce yüklediğiniz tüm Linq-Sql nesnelerinin önbelleğini tutar. Daha önce yüklediğiniz bir kayıtta başka herhangi bir değişiklik yaparsa, en son verileri alamazsınız, yaparsa, kaydı açıkça yeniden olsanız bile alamazsınız!

Bunun nedeni, ObjectTrackingEnabledvarsayılan olarak true olan DataContext üzerinde çağrılan bir özelliktir . Bu özelliği false olarak ayarlarsanız, kayıt her seferinde yeniden yüklenir ... AMA ... SubmitChanges () ile kayıtta herhangi bir değişiklik yapamazsınız.

ANLADIM!


Iv sadece bir buçuk gün (ve bir sürü saç!) Bu HATA kovalayan geçirdi ...
Cerrahi Coder

Buna eşzamanlılık çatışması denir ve bugün bunun etrafında bazı yollar olsa da, hala biraz ağır olma eğiliminde olsa da, bugün hala bir gotcha. DataContext bir kabustu. O_o
Ürdün

17

Stream.Read ile ilgili sözleşme, birçok insanın yolculuğunu gördüğüm bir şey:

// Read 8 bytes and turn them into a ulong
byte[] data = new byte[8];
stream.Read(data, 0, 8); // <-- WRONG!
ulong data = BitConverter.ToUInt64(data);

Bunun yanlış olmasının nedeni , en fazla belirtilen bayt sayısını Stream.Readokuyacağı , ancak akış bitmeden önce başka bir 7 bayt bulunsa bile, yalnızca 1 baytı okumakta tamamen ücretsiz olmasıdır .

O kadar benzer bu görünümün onun yardım etmez Stream.Write, olduğu hiçbir istisna ile dönerse garantili tüm bayt yazdım etmek. Ayrıca yukarıdaki kodun neredeyse her zaman çalışmasına yardımcı olmaz . Ve elbette N baytlarını doğru bir şekilde okumak için hazır ve kullanışlı bir yöntemin bulunmamasına yardımcı olmaz.

Yani, deliği tıkamak ve bunun farkındalığını arttırmak için, bunu yapmanın doğru bir yol örneği:

    /// <summary>
    /// Attempts to fill the buffer with the specified number of bytes from the
    /// stream. If there are fewer bytes left in the stream than requested then
    /// all available bytes will be read into the buffer.
    /// </summary>
    /// <param name="stream">Stream to read from.</param>
    /// <param name="buffer">Buffer to write the bytes to.</param>
    /// <param name="offset">Offset at which to write the first byte read from
    ///                      the stream.</param>
    /// <param name="length">Number of bytes to read from the stream.</param>
    /// <returns>Number of bytes read from the stream into buffer. This may be
    ///          less than requested, but only if the stream ended before the
    ///          required number of bytes were read.</returns>
    public static int FillBuffer(this Stream stream,
                                 byte[] buffer, int offset, int length)
    {
        int totalRead = 0;
        while (length > 0)
        {
            var read = stream.Read(buffer, offset, length);
            if (read == 0)
                return totalRead;
            offset += read;
            length -= read;
            totalRead += read;
        }
        return totalRead;
    }

    /// <summary>
    /// Attempts to read the specified number of bytes from the stream. If
    /// there are fewer bytes left before the end of the stream, a shorter
    /// (possibly empty) array is returned.
    /// </summary>
    /// <param name="stream">Stream to read from.</param>
    /// <param name="length">Number of bytes to read from the stream.</param>
    public static byte[] Read(this Stream stream, int length)
    {
        byte[] buf = new byte[length];
        int read = stream.FillBuffer(buf, 0, length);
        if (read < length)
            Array.Resize(ref buf, read);
        return buf;
    }

1
Ya da, sizin açık örnekte: var r = new BinaryReader(stream); ulong data = r.ReadUInt64();. BinaryReader çok bir FillBufferyöntem var ...
jimbobmcgee

15

Etkinlikler

Olayların neden bir dil özelliği olduğunu hiç anlamadım. Kullanımı karmaşıktır: aramadan önce null olup olmadığını kontrol etmeniz gerekir, kaydını silmeniz gerekir (kendiniz), kimin kayıtlı olduğunu bulamazsınız (örneğin: kayıt oldum mu?). Bir olay neden sadece kütüphanedeki bir sınıf değil? Temelde bir uzman List<delegate>?


1
Ayrıca, çoklu kullanım acı vericidir. Tüm bu sorunlar dışında null-şey CAB'de (özellikleri gerçekten sadece dilde oluşturulmalıdır) giderilir - olaylar global olarak bildirilir ve herhangi bir yöntem kendini herhangi bir olayın "abonesi" olarak ilan edebilir. CAB ile ilgili tek sorunum, global olay adlarının numaralandırmalardan ziyade dize olmasıdır (Java'nın sahip olduğu daha akıllı numaralar tarafından düzeltilebilir, doğal olarak dize olarak çalışır!) . CAB kurmak zordur ama mevcut basit açık kaynak klon var burada .
BlueRaja - Danny Pflughoeft

3
.Net olaylarının uygulanmasından hoşlanmıyorum. Olay aboneliği, aboneliği ekleyen ve Dispose'd olduğunda aboneliği silecek bir IDisposable döndüren bir yöntem çağrılarak ele alınmalıdır. Anlamsallıkları biraz tehlikeli olabilen bir "ekle" ve "kaldır" yöntemini birleştiren özel bir yapıya gerek yoktur, özellikle de bir çok noktaya yayın delegesi eklemeye ve daha sonra kaldırmaya çalışırsa (örn. "B" ve ardından "AB" ekleyin, sonra kaldırın "B" ("BA" 'dan ayrılıyor) ve "AB" (hala "BA"' dan ayrılıyor) Hata!
supercat

@supercat Nasıl yeniden yazardınız button.Click += (s, e) => { Console.WriteLine(s); }?
Ark-kun

Diğer etkinliklerden ayrı olarak IEventSubscription clickSubscription = button.SubscribeClick((s,e)=>{Console.WriteLine(s);});abonelikten çıkabilmem ve aracılığıyla abonelikten çıkabilmem gerekirse clickSubscription.Dispose();. Nesnem tüm abonelikleri kullanım ömrü boyunca saklarsa MySubscriptions.Add(button.SubscribeClick((s,e)=>{Console.WriteLine(s);}));ve sonra MySubscriptions.Dispose()tüm abonelikleri öldürürse.
supercat

@ Ark-kun: Dış abonelikleri kapsülleyen nesneleri tutmak sıkıntı verici gibi görünebilir, ancak abonelikler olarak tüzel kişilikler, bunların temizlenmesini sağlayacak bir türle birleştirilmesini mümkün kılabilir, aksi takdirde çok zor bir şey.
supercat

14

Bugün uzun süre kaçan bir hatayı düzelttim. Hata, çok iş parçacıklı senaryoda kullanılan genel bir sınıftaydı ve Interlocked kullanarak kilitsiz eşitleme sağlamak için statik bir int alanı kullanıldı. Hata, bir tür için genel sınıfın her örneğinin kendi statikine sahip olmasından kaynaklandı. Böylece her iş parçacığının kendi statik alanı vardır ve istendiği gibi bir kilit kullanılmamıştır.

class SomeGeneric<T>
{
    public static int i = 0;
}

class Test
{
    public static void main(string[] args)
    {
        SomeGeneric<int>.i = 5;
        SomeGeneric<string>.i = 10;
        Console.WriteLine(SomeGeneric<int>.i);
        Console.WriteLine(SomeGeneric<string>.i);
        Console.WriteLine(SomeGeneric<int>.i);
    }
}

Bu baskı 5 10 5


5
statikleri tanımlayan ve jenerikleri ondan alan jenerik olmayan bir temel sınıfınız olabilir. Her ne kadar ben C # bu davranış için düştü - Hala bazı C ++ şablonları uzun hata ayıklama saatleri hatırlıyorum ... Eww! :)
Paulius

7
Tuhaf, bunun çok açık olduğunu düşündüm. Sadece itürü olsaydı ne yapması gerektiğini düşün T.
Timwi

1
Type parametresi, öğesinin bir parçasıdır Type. SomeGeneric<int>farklı bir Tür SomeGeneric<string>; yani tabii ki her biri kendipublic static int i
radarbob

13

Numaralandırıcılar bir kereden fazla değerlendirilebilir

Tembel olarak numaralandırılmış bir numaralandırmanız olduğunda sizi ısırır ve iki kez tekrarlar ve farklı sonuçlar alırsınız. (veya aynı sonuçları alırsınız ancak gereksiz yere iki kez çalışır)

Örneğin, belirli bir testi yazarken, mantığı test etmek için birkaç geçici dosyaya ihtiyacım vardı:

var files = Enumerable.Range(0, 5)
    .Select(i => Path.GetTempFileName());

foreach (var file in files)
    File.WriteAllText(file, "HELLO WORLD!");

/* ... many lines of codes later ... */

foreach (var file in files)
    File.Delete(file);

Sürprizimi ne zaman hayal et File.Delete(file) atarFileNotFound !!

Burada olan, filesnumaralandırılabilir olanın iki kez yinelenmesidir (ilk yinelemenin sonuçları basitçe değil hatırladı) ve her yeni tekrarında Eğer yeniden arayarak olurdu Path.GetTempFilename()Eğer geçici dosya adları farklı bir dizi alırsınız.

Çözüm, elbette, ToArray()veya kullanarak değeri istekli olarak numaralandırmaktır ToList():

var files = Enumerable.Range(0, 5)
    .Select(i => Path.GetTempFileName())
    .ToArray();

Çok iş parçacıklı bir şey yaptığınızda bu daha da korkutucu:

foreach (var file in files)
    content = content + File.ReadAllText(file);

ve sen content.Lengthtüm yazmadan sonra hala 0 olduğunu bulmak !! O zaman bir yarış koşulu olmadığını titizlikle kontrol etmeye başlar .... bir saat boşa sonra ... sadece unuttum o küçük küçük Numaralandırılabilir gotcha şey anladım ....


Bu tasarım gereğidir. Buna ertelenmiş icra denir. Diğer şeylerin yanı sıra, TSQL yapılarını simüle etmek için tasarlanmıştır. Bir sql görünümünden her seçiminizde farklı sonuçlar elde edersiniz. Ayrıca SQL Server gibi uzak veri depoları için yararlı olan zincirleme işlemine izin verir. Aksi takdirde x.Select.Where.OrderBy veritabanına 3 ayrı komut gönderir ...
as9876

@AYS soru başlığında "Gotcha" kelimesini kaçırdınız mı?
chakrit

Gotcha'nın kasıtlı bir şey değil tasarımcıların gözetimi anlamına geldiğini düşündüm.
as9876

Belki yeniden başlatılamayan IEnumerables için başka bir tür olmalıdır. AutoBufferedEnumerable gibi mi? Kolayca uygulanabilir. Bu, çoğunlukla programcının bilgi eksikliğinden kaynaklanıyor gibi görünüyor, mevcut davranışta yanlış bir şey olduğunu sanmıyorum.
Eldritch Conundrum

13

Sadece bir süre hata ayıklamada sıkışmış garip bir tane buldum:

Bir dışlama atmadan boş bir int için null değerini artırabilirsiniz ve değer null olarak kalır.

int? i = null;
i++; // I would have expected an exception but runs fine and stays as null

Bu, C # 'ın null olabilecek türler için işlemleri nasıl kullandığının sonucudur. NaN'ye biraz benziyor, ona attığınız her şeyi tüketiyor.
IllidanS4, Monica'yı

10
TextInfo textInfo = Thread.CurrentThread.CurrentCulture.TextInfo;

textInfo.ToTitleCase("hello world!"); //Returns "Hello World!"
textInfo.ToTitleCase("hElLo WoRld!"); //Returns "Hello World!"
textInfo.ToTitleCase("Hello World!"); //Returns "Hello World!"
textInfo.ToTitleCase("HELLO WORLD!"); //Returns "HELLO WORLD!"

Evet, bu davranış belgelenmiştir, ancak bu kesinlikle doğru değildir.


5
Kabul etmiyorum - bir sözcük tümüyle yazıldığında, Başlık Davası ile uğraşmak istemediğiniz özel bir anlamı olabilir, örneğin "ABD Başkanı" -> "ABD Başkanı" değil, "ABD Başkanı Amerika Birleşik Devletleri".
Shaul Behr

5
@Shaul: Bu durumda, karışıklığı önlemek için bunu bir parametre olarak belirtmelidirler , çünkü bu davranışı vaktinde bekleyen hiç kimseyle tanışmadım - bu bir gotcha !
BlueRaja - Danny Pflughoeft
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.