programing

C# 또는 . 최악의 gotchain입니다.NET?

newstyles 2023. 5. 14. 10:26

C# 또는 . 최악의 gotchain입니다.NET?

저는 최근에 한 사람과 함께 일했습니다.DateTime반대하고 다음과 같은 글을 썼습니다.

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

다음대인트라설서명스에 대한 AddDays()날짜에 하루를 추가한다고 하지만 실제로는 날짜가 추가된 날짜를 반환하므로 다음과 같이 작성해야 합니다.

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

이것은 이전에도 여러 번 저를 물었기 때문에 최악의 C# gotchas 목록을 만드는 것이 유용할 것이라고 생각했습니다.

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

젠장, 앱이 스택 추적 없이 충돌합니다.항상 일어나는 일이야.

)MyVar 문자대에 myVar처음부터.)

Type.GetType

많은 사람들이 물어뜯는 것을 본 적이 있는 것은.그들은 왜 그것이 그들 자신의 어셈블리에 있는 유형들과 같은 어떤 유형들에 대해 작동하는지 궁금해합니다.System.String하지만 아닙니다.System.Windows.Forms.Form정답은 현재 어셈블리와 내부에서만 확인됩니다.mscorlib.


익명 메서드

C# 2.0은 익명 방식을 도입하여 다음과 같은 불쾌한 상황을 초래했습니다.

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

그것은 무엇을 출력합니까?글쎄요, 그건 전적으로 일정에 달려 있습니다.10개의 숫자를 인쇄할 수는 있지만 0, 1, 2, 3, 4, 5, 6, 7, 8, 9는 인쇄할 수 없습니다.문는그것라는 입니다.i대리자를 생성하는 시점의 값이 아니라 캡처된 변수입니다.이 문제는 적절한 범위의 추가 로컬 변수를 사용하여 쉽게 해결할 수 있습니다.

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

반복기 블록 실행 지연

이 "가난한 사람의 유닛 테스트"는 통과하지 못합니다 - 왜 통과하지 못합니까?

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

정답은 소스 내의 코드입니다.CapitalLetters코드는 반복기가 실행될 때까지 실행되지 않습니다.MoveNext()메서드가 먼저 호출됩니다.

는 제 두뇌 학습자 페이지에 다른 이상한 점들이 좀 있습니다.

하이젠베르크 시계창

이는 다음과 같은 주문형 로드 작업을 수행하는 경우 심각한 문제가 될 수 있습니다.

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

이제 다음을 사용하는 코드가 있다고 가정해 보겠습니다.

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

이제디를를것을는려수(행)를 .CreateMyObj() 코드를 요.그래서 당신은 코드에 발을 들여놓을 의도로 위의 3행에 중단점을 두었습니다.은 또한 측정을 ._myObj = CreateMyObj();그리고 심지어 내부의 중단점.CreateMyObj()그 자체로

코드는 3호선의 중단점에 도달합니다.당신은 코드에 발을 들여놓습니다.조건부 코드를 입력해야 합니다. 왜냐하면_myObj분명히 무효입니다, 그렇죠?어... 그래서...왜 그것은 조건을 건너뛰고 곧장.return _myObj당신은 당신의 마우스를 _myObj... 위에올 다▁indeed니. 그리고 실제로,그리고 실제로, 그것은 가치가 있습니다!어떻게 그런 일이?!

"감시" 창이 열려 있기 때문에 IDE가 값을 얻도록 했다는 것입니다. 특히 현재 또는 이전 실행 라인과 관련된 모든 변수/속성의 값을 표시하는 "자동" 감시 창이 열립니다.당신이 3호선의 중단점에 도달했을 때, 시계 창은 당신이 그 가치를 알고 싶어할 것이라고 결정했습니다.MyObj그래서 당신의 어떤 중단점도 무시한 채, 무대 뒤로 가서 가치를 계산했습니다.MyObj_myObj!값을 설정하는 호출을 포함하여 당신을 위해.

그것이 제가 이것을 하이젠베르크 시계창이라고 부르는 이유입니다 - 당신은 그것에 영향을 주지 않고는 그 가치를 관찰할 수 없습니다...:)

알았어. 거짓말하지 마.


편집 - @Christian Hayter의 논평은 이 문제에 대한 효과적인 해결책처럼 보이기 때문에 주요 답변에 포함될 가치가 있다고 생각합니다.그래서 당신이 게으른 재산을 가지고 있을 때마다...

[디버거 브라우징 가능(디버거 브라우징 가능 상태)]으로 속성을 장식합니다.없음)] 또는 [디버거 디스플레이("<요청 시 로드됨>")].크리스티안 헤이터

예외 다시 던지기

많은 새로운 개발자들을 얻는 갓챠는 리던시 예외 의미론입니다.

다음과 같은 코드를 자주 봅니다.

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

문제는 스택 추적을 지우고 문제를 진단하는 것을 훨씬 어렵게 만든다는 것입니다. 예외가 발생한 위치를 추적할 수 없기 때문입니다.

올바른 코드는 인수가 없는 스로우 문입니다.

catch(Exception)
{
    throw;
}

또는 예외를 다른 예외로 래핑하고 내부 예외를 사용하여 원래 스택 추적을 가져옵니다.

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

여기 또 다른 시간이 있습니다.

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

시간 범위.는 시간 범위의 초 부분입니다(2분 및 0초 값은 0입니다).

시간 범위.TotalSeconds는 초 단위로 측정된 전체 시간 범위입니다(2분은 총 초 값이 120입니다).

이벤트를 해제하지 않았기 때문에 메모리가 새는 중입니다.

이것은 심지어 제가 아는 몇몇 선배 개발자들을 붙잡았습니다.

많은 것들이 들어있는 WPF 양식을 상상해 보세요. 그리고 그 안의 어딘가에서 당신은 이벤트를 구독합니다.구독을 취소하지 않으면 전체 양식이 닫히고 참조가 취소된 후 메모리에 저장됩니다.

제가 본 문제는 WPF 양식으로 Dispatch Timer를 만들고 Tick 이벤트에 가입하는 것이었습니다. 타이머에서 -=를 수행하지 않으면 양식 메모리가 누출됩니다!

이 예에서 티어다운 코드는 다음과 같아야 합니다.

timer.Tick -= TimerTickEventHandler;

이것은 WPF 양식 내에 DispatchTimer 인스턴스를 생성했기 때문에 특히 까다롭기 때문에 가비지 컬렉션 프로세스에서 처리하는 내부 참조라고 생각할 수 있습니다.안타깝게도 DispatchTimer는 UI 스레드에서 정적 내부 구독 및 서비스 요청 목록을 사용하므로 참조는 정적 클래스에서 '소유'됩니다.

아마도 MSDN에 그 행동이 분명하게 쓰여져 있기 때문에 실제로는 고트카가 아닐 수도 있지만, 그것이 다소 직관적이지 않다는 것을 발견했기 때문에 제 목이 부러진 적이 있습니다.

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

이 남자는 떠나갑니다."nice.pic"이미지가 삭제될 때까지 파일이 잠겨 있습니다.그 당시에는 아이콘을 즉시 로드하는 것이 좋을 것이라고 생각했지만 (처음에는) 수십 개의 파일이 열려 있고 잠겨 있다는 사실을 깨닫지 못했습니다.이미지는 파일을 로드한 위치를 추적합니다...

어떻게 해결합니까?저는 한 대의 정기선이 그 일을 해낼 것이라고 생각했습니다.다음에 대한 추가 매개 변수를 예상했습니다.FromFile()하지만 아무것도 없었어요 그래서 이걸 썼어요...

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

ASP를 세어보면요.NET, 저는 웹 양식의 라이프사이클이 저에게 큰 혼란이라고 말하고 싶습니다.많은 개발자들이 어떤 이벤트 핸들러(슬프게도 저를 포함해서)를 사용해야 하는지 잘 모르기 때문에 저는 서투르게 작성된 웹 양식 코드를 디버깅하는 데 수 많은 시간을 보냈습니다.

오버로드된 == 운영자 및 유형화되지 않은 컨테이너(어레이 목록, 데이터 세트 등):

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

해결책?

  • 항상사는하를 사용합니다.string.Equals(a, b) 유형을 때 문열 유비 교할때 을자형 때

  • 와 같은 제네릭을 사용하여.List<string>두 피연산자가 모두 문자열인지 확인합니다.

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

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

이야기의 교훈: 객체를 역직렬화할 때 필드 이니셜라이저가 실행되지 않습니다.

날짜 시간.To String("dd/MM/yyyy");이것은 실제로 항상 dd/MM/yyyy를 제공하지는 않지만 대신 지역 설정을 고려하고 위치에 따라 날짜 구분 기호를 대체합니다.그래서 당신은 dd-MM-yyyy 또는 비슷한 것을 얻을 수 있습니다.

올바른 방법은 DateTime을 사용하는 것입니다.문자열로("dd'/')MM'/'yyyy";


날짜 시간.ToString("r")은 GMT를 사용하는 RFC1123으로 변환되어야 합니다. GMT는 UTC에서 몇 초 이내이지만 "r" 형식 지정자는 문제의 DateTime이 Local로 지정되더라도 UTC로 변환되지 않습니다.

따라서 다음과 같은 gotcha가 발생합니다(현지 시간이 UTC에서 얼마나 멀리 떨어져 있는지에 따라 다름).

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

이런!

일전에 올라온 이 글을 봤는데, 잘 모르는 사람들에게는 꽤 애매하고 고통스럽다고 생각합니다.

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

대부분의 예상대로 1이 아닌 0이 반환됩니다.

이 파티에 좀 늦었지만, 최근에 저를 괴롭힌 두 명의 고트카가 있습니다.

DateTime 확인

Ticks 속성은 1,000만 분의 1초(100나노초 블록)의 시간을 측정하지만 해상도는 100나노초가 아니라 약 15ms입니다.

다음 코드:

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

는 다음과 같은 출력을 제공합니다(예:

0
0
0
0
0
0
0
156254
156254
156254

마찬가지로, DateTime을 보면 그렇습니다.이제 밀리초 단위로 15.625ms의 둥근 청크 값을 얻을 수 있습니다. 15, 31, 46 등입니다.

특정 동작은 시스템마다 다르지만 이 날짜/시간 API에는 다른 해상도 관련 gotch가 있습니다.


경로.결합

파일 경로를 결합하는 좋은 방법이지만 항상 예상대로 작동하는 것은 아닙니다.

가 두번 변매다로 \캐릭터, 그것은 당신에게 완전한 길을 주지 않을 것입니다.

다음 코드:

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

다음 출력을 제공합니다.

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

프로세스를 시작할 때(시스템 사용).Diagnostics)는 콘솔에 기록되지만 콘솔은 읽지 않습니다.아웃스트림, 일정량의 출력 후에 앱이 중단된 것처럼 보일 것입니다.

Linkq-To-Sql에 연산자 바로 가기 없음

여기 보세요.

쿼리의 절에서는 Linq-To-Sql 쿼리와 조건부 할 수 .||그리고.&&Null 참조 예외를 방지하기 위해, 첫 번째 조건이 두 번째 조건을 평가할 필요가 없는 경우에도 Link-To-Sql은 OR 또는 AND 연산자의 양쪽을 평가합니다!

가상 메서드에 기본 매개 변수 사용

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();

력출:
기저

변수 컬렉션의 값 개체

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

mypoints[i].x = 10;

효과가 없습니다.

mypoints[i]을 반다니합환의 합니다.Pointobject.value 체객.C#을 사용하여 복사본의 필드를 수정할 수 있습니다.묵묵히 아무것도 하지 않고 있습니다.


업데이트: 이 문제는 C# 3.0에서 해결된 것으로 보입니다.

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

최악은 아닐 수도 있지만, .net 프레임워크의 일부는 학위를 사용하는 반면 다른 부분 라디안을 사용합니다(그리고 IntelliSense와 함께 표시되는 설명서는 어떤 것인지 알려주지 않으므로 MSDN을 방문해야 합니다).

이 모든 것을 피할 수 있었을 것입니다.Angle대신 수업...

C/C++ 프로그래머에게 C#로의 전환은 자연스러운 것입니다.하지만 제가 개인적으로 마주친 (그리고 다른 사람들과 같은 전환을 하는 것을 목격한) 가장 큰 문제는 C#의 클래스와 구조 사이의 차이를 완전히 이해하지 못한다는 것입니다.

C++에서 클래스와 구조체는 동일하며 기본 가시성만 다릅니다. 여기서 클래스는 기본 가시성으로 기본 설정되고 구조체는 기본 공개 가시성으로 기본 설정됩니다.C++에서 이 클래스 정의는

    class A
    {
    public:
        int i;
    };

는 이 구조 정의와 기능적으로 동일합니다.

    struct A
    {
        int i;
    };

그러나 C#에서 클래스는 참조 유형인 반면 구조체는 값 유형입니다.이는 (1) 서로 사용할 시기 결정, (2) 물체 동등성 테스트, (3) 성능(예: 복싱/복싱 해제) 에서 큰 차이를 만듭니다.

웹에는 둘 사이의 차이와 관련된 모든 종류의 정보가 있습니다(: 여기).저는 C#로 전환하는 모든 사람이 최소한 차이점과 그 영향에 대한 실무 지식을 갖추기를 강력히 권장합니다.

가비지 수집 및 폐기().메모리를 확보하기 위해 아무것도 할 필요가 없지만 Dispose()를 통해 리소스를 확보해야 합니다.WinForms를 사용하거나 어떤 방식으로든 개체를 추적할 때 잊기가 매우 쉽습니다.

는 어가구를 구현합니다.IList

하지만 실행하지 마십시오.추가를 호출하면 작동하지 않는다고 알려줍니다.그렇다면 클래스는 인터페이스를 지원할 수 없는데 왜 인터페이스를 구현합니까?

컴파일하지만 작동하지 않음:

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

WCF(Serializer)가 모든 IList를 어레이로 변환하고 런타임 오류가 발생하기 때문에 이 문제가 자주 발생합니다.

각 루프 변수 범위에 대해!

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());

다음 예제는 정상적으로 작동하는 동안 5개의 "amet"를 인쇄합니다.

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());

MS SQL Server에서 1753년 이전 날짜를 처리할 수 없습니다.중요한 점은 이가 와 동기화되지 않았다는 점입니다.그물DateTime.MinDate상수, 1/1/1입니다.그래서 만약 당신이 마인드메이트, 잘못된 날짜(데이터 가져오기에서 최근에 나에게 발생한 것처럼) 또는 단순히 정복자 윌리엄의 생일을 저장하려고 한다면, 당신은 곤경에 처할 것입니다.기본 제공되는 해결 방법은 없습니다. 1753년 이전 날짜를 사용하여 작업해야 할 경우 사용자가 직접 해결 방법을 작성해야 합니다.

스트림의 계약.독서는 제가 많은 사람들을 넘어뜨리는 것을 본 적이 있습니다.

// 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);

는 이이잘이유 때문입니다.Stream.Read최대 지정된 바이트 수를 읽지만 스트림 종료 전에 다른 7바이트를 사용할 수 있는 경우에도 1바이트만 자유롭게 읽을 수 있습니다.

이것이 매우 유사하게 보이는 것은 도움이 되지 않습니다.Stream.Write예외 없이 반환되는 경우 모든 바이트를 기록했음이 보장됩니다.위의 코드가 거의 항상 작동한다는 것도 도움이 되지 않습니다.그리고 물론 정확하게 N바이트를 정확하게 읽기 위한 이미 만들어진 편리한 방법이 없다는 것은 도움이 되지 않습니다.

따라서 구멍을 막고 이에 대한 인식을 높이기 위해 올바른 방법의 예를 다음에 제시합니다.

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

고약한 린크 캐싱 갓챠

이 발견을 이끈 제 질문과 문제를 발견한 블로거보십시오.

즉, DataContext는 로드한 모든 Linq-to-Sql 개체의 캐시를 유지합니다.이전에 로드한 레코드를 다른 사용자가 변경한 경우 레코드를 명시적으로 다시 로드하더라도 최신 데이터를 얻을 수 없습니다!

이는 다음과 같은 속성 때문입니다.ObjectTrackingEnabled기본적으로 참인 데이터 컨텍스트에 있습니다.해당 속성을 false로 설정하면 레코드가 매번 새로 로드됩니다.그러나... 변경사항 제출()을 사용하여 해당 레코드의 변경사항을 유지할 수 없습니다.

알았어. 거짓말하지 마.

이벤트

저는 왜 이벤트가 언어 기능인지 이해할 수 없었습니다.사용하기가 복잡합니다. 전화하기 전에 null을 확인해야 하며, 등록을 취소해야 합니다. 등록된 사람을 찾을 수 없습니다(예: 제가 등록했습니까?).왜 이벤트는 도서관에서 그냥 수업이 아닌가요?된 기으로전된화문적.List<delegate>?

오늘 나는 오랫동안 빠져나간 버그를 고쳤습니다.이 버그는 멀티 스레드 시나리오에서 사용되는 일반 클래스에 있었으며 인터락을 사용하여 잠금이 없는 동기화를 제공하기 위해 정적 int 필드가 사용되었습니다.이 버그는 유형에 대한 일반 클래스의 각 인스턴스화에 고유한 정적이 있기 때문에 발생했습니다.따라서 각 스레드는 고유한 정적 필드를 가지고 있으며 의도한 대로 잠금을 사용하지 않았습니다.

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

이것은 510 5를 인쇄합니다.

잠시 디버그에 빠진 이상한 것을 발견했습니다.

예외를 발생시키지 않고 null 가능한 int에 대해 null을 증분할 수 있으며 값은 null로 유지됩니다.

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

열거형은 두 번 이상 평가할 수 있습니다.

그것은 당신이 게으르게 열거된 열거형을 가지고 있고 당신이 그것을 두 번 반복하고 다른 결과를 얻을 때 당신을 물 것입니다.(또는 동일한 결과를 얻지만 불필요하게 두 번 실행됨)

예를 들어, 특정 테스트를 작성하는 동안 로직을 테스트하기 위해 몇 개의 임시 파일이 필요했습니다.

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

내가 깜짝 놀랐을 때를 상상해 보세요.File.Delete(file)FileNotFound!!

여기서 일어나는 일은files열거형은 두 번 반복되며(첫 번째 반복의 결과는 단순히 기억되지 않음), 각 새로운 반복에서 다시 반복됩니다.Path.GetTempFilename()따라서 다른 일련의 임시 파일 이름을 얻을 수 있습니다.

물론 솔루션은 다음을 사용하여 가치를 열심히 열거하는 것입니다.ToArray()또는ToList():

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

이는 다음과 같은 멀티스레드 작업을 수행할 때 더욱 무섭습니다.

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

그리고 당신은 알게 됩니다.content.Length모든 쓰기 후에도 여전히 0입니다!!그런 다음 경주 조건이 없는지 엄격하게 점검하기 시작합니다. 한 시간을 허비한 후에...당신은 작은 열거형이 당신이 잊어버린 물건을 가지고 있다는 것을 알게 되었습니다.

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!"

네, 이 행동은 문서화되어 있지만, 그렇다고 해서 제대로 된 것은 아닙니다.

언급URL : https://stackoverflow.com/questions/241134/what-is-the-worst-gotcha-in-c-sharp-or-net