Programing

C # 또는 .NET에서 최악의 문제는 무엇입니까?

lottogame 2020. 2. 26. 07:40
반응형

C # 또는 .NET에서 최악의 문제는 무엇입니까? [닫은]


최근에 DateTime객체로 작업하고 있었고 다음과 같이 썼습니다.

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

intellisense 문서에 AddDays()따르면 날짜에 날짜를 추가하지만 날짜가 추가되지 않은 날짜를 반환 하므로 실제로 다음과 같이 작성해야합니다.

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

이것은 전에 여러 번 물린 적이 있으므로 최악의 C # gotcha를 카탈로그 화하는 것이 유용 할 것이라고 생각했습니다.


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

블 람모. 스택 추적없이 앱이 충돌합니다. 항상 일어난다.

( 게터에서 MyVar소문자 대신 통지 자본 myVar)


Type.GetType

내가 많은 사람들을 물린 것을 보았습니다 Type.GetType(string). 그들은 왜 자신의 어셈블리에서 유형에 대해 작동하는지 궁금 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()메소드가 처음 호출 될 때까지 코드 소스 내의 코드가 실행되지 않는다는 것 입니다.

brainteasers 페이지 에 다른 이상한 점이 있습니다.


예외 다시 던지기

많은 새로운 개발자를 얻는 문제는 다시 던지기 예외 의미입니다.

많은 시간이 다음과 같은 코드를 본다

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

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

올바른 코드는 인수가없는 throw 문입니다.

catch(Exception)
{
    throw;
}

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

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

하이젠 베르크 시계 창

다음과 같이 주문형 작업을 수행하는 경우 심하게 물릴 수 있습니다.

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분명히 null 이기 때문에 조건부 코드를 입력 할 것으로 예상됩니다 . 어 ... 그래서 ... 왜 조건을 건너 뛰고 곧장 갔 return _myObj습니까?! _myObj 위로 마우스를 가져 가면 실제로 가치가 있습니다! 어떻게 된거 지?!

답은 "감시"창이 열려 있기 때문에 IDE가 값을 얻었 기 때문입니다. 특히 "자동"감시 창은 현재 또는 이전 실행 라인과 관련된 모든 변수 / 속성 값을 표시합니다. Line 3에서 중단 점에 도달하면 감시 창에서 중단 점을 무시하고MyObj 장면 뒤의 가치를 알고 싶다고 결정했습니다. 중단 점을 무시 하고 호출 한 값을 포함하여MyObj 사용자 의 가치를 계산 했습니다. _myObj! 값을 설정합니다CreateMyObj()

이것이 제가 이것을 하이젠 버그 시계 창이라고 부르는 이유입니다-당신은 그 값에 영향을 미치지 않으면 서 그 값을 관찰 할 수 없습니다 ... :)

고차!


편집 -@ChristianHayter의 의견은이 문제에 대한 효과적인 해결 방법처럼 보이기 때문에 주요 답변에 포함될 가치가 있다고 생각합니다. 그래서 당신은 게으른로드 속성이있을 때마다 ...

[DebuggerBrowsable (DebuggerBrowsableState.Never)] 또는 [DebuggerDisplay ( "<loaded on demand>")]로 속성을 장식하십시오. – 크리스티안 헤이 터


나를 데려 오는 또 다른 시간이 있습니다.

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

TimeSpan.Seconds 는 시간 범위의 초 부분입니다 (2 분 및 0 초는 초 값이 0 임).

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


이벤트를 후크 해제하지 않았기 때문에 메모리 누수가 발생합니다.

이것은 심지어 내가 아는 선임 개발자들을 사로 잡았습니다.

많은 것들이 포함 된 WPF 양식을 상상해 보시고 어딘가에서 이벤트를 구독하십시오. 구독을 취소하지 않으면 양식을 닫고 참조 해제 한 후에 전체 양식이 메모리에 보관됩니다.

내가 본 문제는 WPF 양식에서 DispatchTimer를 만들고 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을 계산하면 webforms 라이프 사이클이 꽤 큰 문제라고 생각합니다. 잘못 작성된 웹 양식 코드를 디버깅하는 데 많은 시간을 보냈습니다. 많은 개발자가 언제 어떤 이벤트 핸들러 (슬프게도 포함)를 사용 해야하는지 실제로 이해하지 못하기 때문입니다.


과부하 == 연산자 및 유형이 지정되지 않은 컨테이너 (배열 목록, 데이터 집합 등) :

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

이야기의 교훈 : 객체를 역 직렬화 할 때 필드 초기화가 실행되지


DateTime.ToString ( "dd / MM / yyyy") ; 이것은 실제로 것입니다 하지 항상 당신이 / DD MM / YYYY 대신 당신은 위치에 따라 날짜 구분 계정에 국가 별 설정을하고 대체합니다 제공합니다. 따라서 dd-MM-yyyy 또는 이와 유사한 것을 얻을 수 있습니다.

올바른 방법은 DateTime.ToString ( "dd '/'MM '/'yyyy");


DateTime.ToString ( "r") 은 GMT를 사용하는 RFC1123으로 변환되어야합니다. GMT는 UTC에서 1 분의 1 초 내에 있지만 "r"형식 지정 자는 해당 DateTime이 Local로 지정된 경우에도 UTC로 변환되지 않습니다 .

결과적으로 다음과 같은 차이가 발생합니다 (현지 시간이 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 천만 분의 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.Now.Millisecond를 보면 15,625ms의 둥근 청크로 값을 얻을 수 있습니다 : 15, 31, 46 등

이 특정 동작은 시스템에 따라 다릅니다 만, 다른 해상도 관련 망 가지고있다 이 날짜 / 시간 API에가.


Path.Combine

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

두 번째 매개 변수가 \문자로 시작 하면 완전한 경로를 제공하지 않습니다.

이 코드는 :

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\

콘솔에 쓰는 프로세스 (System.Diagnostics 사용)를 시작하지만 Console.Out 스트림을 읽지 않으면 일정량의 출력 후에 앱이 정지 된 것처럼 보입니다.


Linq-To-Sql에는 운영자 단축키가 없습니다.

여기를 참조 하십시오 .

간단히 말해서 Linq-To-Sql 쿼리의 조건절 내에서 null 참조 예외 ||같은 조건부 바로 가기를 사용할 수 없습니다 &&. Linq-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]Point값 객체 의 복사본을 반환 합니다. 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) 성능 (예 : 복싱 / 언 박싱) 등에서 차이를 만듭니다 .

웹 (예 : here ) 의 차이점과 관련된 모든 종류의 정보가 웹에 있습니다 . 최소한 C #으로 전환하는 사람은 적어도 차이점과 그 의미에 대한 실무 지식을 갖도록 강력히 권장합니다.


가비지 콜렉션 및 Dispose (). 메모리 를 확보하기 위해 아무 것도 할 필요는 없지만 Dispose ()를 통해 리소스확보 해야 합니다 . WinForms를 사용하거나 어떤 방식 으로든 객체를 추적 할 때 잊어 버리는 것은 매우 쉬운 일입니다.


foreach 루프 변수 범위!

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

다섯 "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 년 이전의 날짜를 처리 할 수 ​​없습니다. 중요한 것은 .NET DateTime.MinDate상수 ( 1/1/1)와 동기화되지 않은 것입니다 . 따라서 마음에 들거나 잘못된 날짜 (최근에 데이터 가져 오기에서 발생 했음) 또는 정복자 윌리엄의 생년월일을 저장하려고하면 문제가 생길 수 있습니다. 이에 대한 기본 해결 방법은 없습니다. 1753 년 이전의 날짜로 작업해야 할 경우 자체 해결 방법을 작성해야합니다.


배열 구현 IList

그러나 그것을 구현하지 마십시오. 추가를 호출하면 작동하지 않는다는 메시지가 나타납니다. 그렇다면 왜 클래스가 인터페이스를 지원할 수 없을 때 인터페이스를 구현합니까?

컴파일하지만 작동하지 않습니다.

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

serializer (WCF)가 모든 IList를 배열로 바꾸고 런타임 오류가 발생하기 때문에이 문제가 많이 있습니다.


불쾌한 Linq 캐싱 잡기

참조 내 질문 이 발견되었다 것을, 그리고 블로거 문제를 발견했다.

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

이는 ObjectTrackingEnabled기본적으로 true 인 DataContext에서 호출 특성 때문입니다 . 해당 속성을 false로 설정하면 매번 레코드가 새로로드됩니다 ... 그러나 ... SubmitChanges ()를 사용하여 해당 레코드의 변경 내용을 유지할 수 없습니다.

고차!


Stream.Read의 계약 은 많은 사람들이 여행하는 것을 보았습니다.

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

행사

왜 이벤트가 언어 기능인지 이해하지 못했습니다. 그것들은 사용하기가 복잡합니다 : 당신은 전화하기 전에 null을 확인해야합니다, 당신은 (자신의) 등록을 취소해야합니다, 당신은 누가 등록되었는지 찾을 수 없습니다 (예 : 내가 등록 했습니까?) 도서관에서 이벤트가 수업이 아닌 이유는 무엇입니까? 기본적으로 전문 List<delegate>?


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

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

이 인쇄 5 5 5


열거 가능 항목을 두 번 이상 평가할 수 있습니다

지연 열거 형 열거 형이 있고 두 번 반복하여 다른 결과를 얻을 때 물릴 것입니다. (또는 동일한 결과를 얻지 만 불필요하게 두 번 실행됩니다)

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

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이라는 것을 알았습니다! 그런 다음 한 시간을 낭비한 후에도 경쟁 조건이 없는지 엄격하게 확인하기 시작합니다. 잊어 버린 작은 작은 열거 형 일뿐이라는 것을 알았습니다.


잠시 동안 디버그에 갇힌 이상한 것을 발견했습니다.

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

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

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



반응형