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


377

최근에 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 # gotcha를 카탈로그 화하는 것이 유용 할 것이라고 생각했습니다.


157
반환 DateTime.Now.AddDays (1);
crashmstr

23
내장 된 값 유형 인 AFAIK는 최소한 유형에 포함 된 메소드가 기존 항목을 수정하지 않고 새 항목을 리턴한다는 점에서 모두 불변입니다. 적어도, 나는 이것을하지 않는 머리 꼭대기에서 하나는 생각할 수 없다. 모두 훌륭하고 일관성이있다.
Joel Coehoorn

6
가변 값 유형 : System.Collections.Generics.List.Enumerator : ((그렇습니다. 충분히 노력하면 이상하게 동작하는 것을 볼 수 있습니다.)
Jon Skeet

13
지능은 당신에게 필요한 모든 정보를 제공합니다. DateTime 객체를 반환한다고 말합니다. 전달한 것을 변경 한 경우에는 void 메소드가됩니다.
존 크래프트

20
반드시 그런 것은 아닙니다 : StringBuilder.Append (...)는 예를 들어 "this"를 반환합니다. 유창한 인터페이스에서는 매우 일반적입니다.
Jon Skeet

답변:


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

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

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


112
이 사이트에 적절한 SO :)
gbjbaanb

62
나는 개인 회원에게 밑줄을 긋고 많은 도움을줍니다!
chakrit

61
나는 많이 이런 종류의 문제를 중지 내가 할 수있는 자동 속성을 사용)
TWith2Sugars

28
이것은 개인 필드에 접두사를 사용하는 훌륭한 이유입니다 (다른 것들이 있지만 이것이 좋은 것입니다) : _myVar, m_myVar
jrista

205
@ jrista : 오 제발 아니 ... 아니 m_ ... 공포를 aargh ...
fretje

254

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 페이지 에 다른 이상한 점이 있습니다.


25
반복자 예제는 끔찍합니다!
Jimmy

8
왜 이것을 3 개의 답변으로 나누지 않고 모두 함께 투표하지 않고 각각 투표 할 수 있습니까?
chakrit

13
@ chakrit : 돌이켜 보면 아마도 좋은 생각 이었을지 모르겠지만 지금은 너무 늦었다 고 생각합니다. 내가 더 많은 대표를 얻으려고 노력한 것처럼 보일 수도 있습니다 ...
Jon Skeet

19
AssemblyQualifiedName을 제공하면 실제로 Type.GetType이 작동합니다. Type.GetType ( "System.ServiceModel.EndpointNotFoundException, System.ServiceModel, Version = 3.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089");
chilltemp

2
@kentaromiura : 과부하 해결은 가장 파생 된 유형에서 시작하여 트리를 작동하지만 원래 보고 있는 유형으로 선언 된 메소드 만보 고 있습니다. Foo (int)는 기본 메소드를 대체하므로 고려되지 않습니다. Foo (object)가 적용 가능하므로 과부하 해결이 중지됩니다. 이상하다.
Jon Skeet

194

예외 다시 던지기

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

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

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

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

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

catch(Exception)
{
    throw;
}

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

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

운 좋게도 첫 주에 누군가에 의해 이것에 대해 배우고 상급 개발자 코드에서 발견했습니다. Is : catch () {던지기; } 두 번째 코드 스 니펫과 동일합니까? catch (Exception e) {던지기; } Exception 객체를 생성하지 않고 채우지 않습니까?
StuperUser

그냥 던지기 대신 throw ex (또는 throw e)를 사용하는 오류 외에도 다시 던지기 위해 예외를 잡을 가치가있는 경우가 무엇인지 궁금합니다.
Ryan Lundy

13
@Kyralessa : 많은 경우가 있습니다 : 예를 들어, 트랜잭션을 롤백하려는 경우 호출자가 예외를 받기 전에. 롤백 한 다음 다시 던집니다.
R. Martinho Fernandes

7
나는 사람들이 예외를 포착하고 다시 던질 때 항상 이것을 본다. 왜냐하면 그들이 예외를 더 많이 잡아야한다는 것을 깨닫지 않고 모든 예외를 잡아야한다는 것을 배웠기 때문이다. 그것은 나를 미치게합니다.
James Westgate

5
@Kyralessa 가장 큰 경우는 로깅을 수행해야 할 때입니다. 캐치에 오류를 기록하고
다시 던지십시오

194

하이젠 베르크 시계 창

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

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대한 통화를 계산 CreateMyObj()했습니다. _myObj! 값을 설정합니다

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

고차!


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

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


10
훌륭한 발견! 당신은 프로그래머가 아니며, 실제 디버거입니다.
이것. __curious_geek

26
시계 창뿐만 아니라 변수 위로 마우스를 가져가도 마찬가지입니다.
Richard Morgan

31
[DebuggerBrowsable(DebuggerBrowsableState.Never)]또는로 부동산을 장식하십시오 [DebuggerDisplay("<loaded on demand>")].
Christian Hayter

4
프레임 워크 클래스를 개발 중이고 느리게 구성된 속성의 런타임 동작을 변경하지 않고 감시 창 기능을 원하는 경우 디버거 유형 프록시를 사용하여 값이 이미 생성 된 경우 값을 반환 할 수 있습니다. 그런 경우에 건설되었습니다. Lazy<T>(특히 그 클래스 Value속성)이 사용되는 경우의 일 예이다.
샘 하웰

4
나는 (어떤 이유로 나는 짐작할 수 없다) 과부하로 객체의 가치를 바꾼 누군가를 회상한다 ToString. 그가 그것을 가리킬 때마다 툴팁은 그에게 다른 가치를 주었다-그는 그것을 알아낼 수 없었다 ...
JNF

144

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

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


1
예, 저도 저를 얻었습니다. TimeSpan.SecondsPart 또는 그것이 무엇을 나타내는 지 더 명확하게 만드는 것이어야한다고 생각합니다.
Dan Diplo

3
다시 읽는이, 나는 왜 경이에있는 TimeSpan경우에도 Seconds 모두에서 속성을. 어쨌든 쥐의 엉덩이에 누가 시간의 초 부분을 줄까요? 임의의 단위 의존적 값입니다. 나는 그것을위한 실제적인 사용을 생각할 수 없다.
MusiGenesis

2
TimeSpan.TotalSeconds가 시간 범위의 총 초 수를 반환한다는 것을 이해합니다.
Ed S.

16
@MusiGenesis 속성이 유용합니다. 시간 간격을 조각으로 표시하려면 어떻게합니까? 예를 들어 타임스 팬은 '3 시간 15 분 10 초'의 지속 시간을 나타냅니다. 초, 시간, 분 속성없이이 정보에 어떻게 액세스 할 수 있습니까?
SolutionYogi

1
비슷한 API 에서 두 가지를 사용 SecondsPart하고 SecondsTotal구별했습니다.
BlueRaja-대니 Pflughoeft

80

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

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

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

내가 본 문제는 WPF 양식에서 DispatchTimer를 만들고 Tick 이벤트를 구독하는 것이라고 생각합니다. 타이머에서-=를 수행하지 않으면 양식에서 메모리가 누출됩니다!

이 예제에서 테어 다운 코드는

timer.Tick -= TimerTickEventHandler;

WPF 양식 내에 DispatchTimer의 인스턴스를 만들었으므로 특히 까다로워서 가비지 수집 프로세스에서 처리하는 내부 참조라고 생각할 것입니다 ... 불행히도 DispatchTimer는 정적 인 내부 구독 및 서비스 목록을 사용합니다. UI 스레드에 대한 요청이므로 정적 클래스에서 참조를 '소유'합니다.


1
비결은 생성 한 모든 이벤트 구독을 항상 해제하는 것입니다. 양식에 의존하기 시작하면 습관에 빠질 수 있으며 언젠가는 이벤트가 필요한 곳에서 이벤트를 공개하는 것을 잊을 수 있습니다.
Jason Williams

3
약한 참조 이벤트에 대한 MS-연결 제안이 여기 내 의견으로는 우리가 완전히 CAB에 의해 사용되는 것과 같은, 약하게 결합 된 하나 믿을 수 없을만큼 가난한 이벤트 모델을 교체해야하지만,이 문제를 해결합니다.
BlueRaja-대니 Pflughoeft

나에게서 +1, 감사합니다! 글쎄, 내가해야했던 코드 검토 작업에 감사하지 않습니다!
밥 데니

@ BlueRaja-DannyPflughoeft 약한 이벤트에는 또 다른 문제가 있습니다-람다를 구독 할 수 없습니다. 당신은 쓸 수 없습니다timer.Tick += (s, e,) => { Console.WriteLine(s); }
Ark-kun

@ Ark-kun 람다는 더 어렵게 만듭니다. 람다를 변수에 저장하고 분해 코드에서 사용해야합니다. 킨다는 람다 작성의 단순함을 파괴하지 않습니까?
Timothy Walters

63

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

10
나는이 행동이 의미가 없다는 것에 동의합니다. "이 동작은 의도적으로 설계된 것"외에 다른 설명을 찾을 수 없습니다.
MusiGenesis

1
이 해결 방법의 장점은 Image.ToStream (정확한 이름을 잊어 버렸습니다)을 호출하려고하면 나중에 작동하지 않습니다.
Joshua

55
일부 코드를 확인해야합니다. 곧 돌아올거야.
Esben Skov Pedersen

7
@EsbenSkovPedersen 간단하지만 재미 있고 건조한 의견. 내 하루를 보냈습니다.
Inisheer

51

ASP.NET을 세면 webforms 라이프 사이클이 꽤 큰 문제라고 생각합니다. 나는 많은 개발자들이 언제 어떤 이벤트 핸들러 (슬프게도 포함되어 있는지)를 사용할지 실제로 이해하지 못하기 때문에 잘못 작성된 웹 양식 코드를 디버깅하는 데 수많은 시간을 보냈습니다.


26
그래서 제가 MVC로 옮겼습니다 ... viewstate 두통 ...
chakrit

29
ASP.NET 문제에 특별히 집중된 다른 질문이 있습니다 (당연히 그렇습니다). ASP.NET의 기본 개념 (웹 응용 프로그램을 개발자를위한 Windows 응용 프로그램처럼 보이게 함)은 너무 잘못 안내되어 "gotcha"로 간주 될 수도 있습니다.
MusiGenesis

1
MusiGenesis 나는 당신의 의견을 백 번 투표 할 수 있기를 바랍니다.
csauve

3
@MusiGenesis 현재 잘못 안내 된 것으로 보이지만 당시 사람들은 웹 응용 프로그램 (핵심 단어 인 응용 프로그램-ASP.NET WebForms는 블로그를 호스팅하도록 실제로 설계되지 않았습니다)이 Windows 응용 프로그램과 동일하게 작동하기를 원했습니다. 이것은 상대적으로 최근에야 변경되었으며 많은 사람들이 여전히 "아주 부족"합니다. 전체 문제는 추상화가 너무 새는이었다이었다 - 웹은 데스크톱 응용 프로그램처럼 작동하지 않았다 너무 많이 는 거의 모든 사람에 혼란을 초래할.
Luaan

1
아이러니하게도 ASP.NET에 대해 처음 본 것은 ASP.NET을 사용하여 블로그 사이트를 얼마나 쉽게 만들 수 있는지 보여주는 Microsoft 비디오입니다!
MusiGenesis

51

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

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>두 피연산자가 모두 문자열인지 확인하기 위해 제네릭 을 사용합니다.


6
거기에 여분의 공백이있어 모든 것을 잘못 만들었습니다. 그러나 공백을 제거하면 "my"+ "string"이 여전히 상수이므로 마지막 줄은 여전히 ​​참입니다.
Jon Skeet

1
ack! 당신이 맞아 :) 좋아, 조금 편집했다.
Jimmy

이러한 용도로 경고가 생성됩니다.
chakrit

11
예, C # 언어의 가장 큰 결함 중 하나는 Object 클래스의 == 연산자입니다. 그들은 우리가 ReferenceEquals를 사용하도록 강요했을 것입니다.
erikkallen

2
고맙게도 2.0 이후로 제네릭을 사용했습니다. 위의 예제에서 ArrayList 대신 List <string>을 사용하는지 걱정할 필요가 없습니다. 게다가 우리는 그것으로부터 성능을 얻었습니다. 나는 항상 레거시 코드에서 ArrayLists에 대한 오래된 참조를 근절하고 있습니다.
JoelC

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

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

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


8
예, 기본 생성자를 실행하지 않는 .NET 직렬화가 싫어요. 생성자를 호출하지 않고 객체를 생성하는 것이 불가능했지만 아쉽지 않습니다.
Roman Starkov

45

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

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


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

결과적으로 다음과 같은 차이가 발생합니다 (현지 시간이 UTC와 거리에 따라 다름).

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

으악!


19
mm을 MM으로 변경-mm는 분이고 MM은 개월입니다. 또 다른 문제는 다음과 같습니다.
Kobi

1
당신이 그것을 몰랐다면 이것이 어떻게 문제가 될 수 있는지 알 수 있습니다 ... 지역 설정과 일치하지 않습니다.
Beska

6
@Beska : 파일에 쓰려고하므로, 지정된 날짜 형식의 특정 형식이어야합니다.
GvS

11
현지화 된 기본값이 다른 방법보다 나쁘다는 의견이 있습니다. 개발자 중 적어도 현지화를 완전히 무시한 코드 현지화 된 시스템에서 다르게 작동 합니다. 이런 식으로 코드가 작동하지 않을 수 있습니다.
Joshua

32
사실 나는이 것 할 수있는 올바른 방법 생각DateTime.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
대니 Pflughoeft - BlueRaja

44

나는이 날짜가 다른 날에 게시되는 것을 보았는데, 잘 모르는 사람들에게는 꽤 애매하고 고통 스럽다고 생각합니다.

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

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


37
나는 그것이 실제로 사람들을 물지 않기를 희망합니다-나는 그들이 처음부터 그것을 쓰지 않기를 정말로 희망합니다! (물론 어쨌든 재미 있습니다.)
Jon Skeet

12
나는 ...이 매우 불분명 생각하지 않는다
크리스 Marasti - 게오르그에게

10
적어도 C #에서는 예기치 않은 결과가 정의됩니다. C ++에서는 0 또는 1이거나 프로그램 종료를 포함한 다른 결과 일 수 있습니다!
James Curran

7
이것은 문제가 아닙니다. x = x ++-> x = x, 증가 x .... x = ++ x-> 증가 x 다음 x = x
Kevin

28
@ 케빈 : 나는 그것이 그렇게 간단하다고 생각하지 않습니다. x = x ++가 x = x에 이어 x ++에 해당하는 경우 결과는 x = 1이됩니다. 대신, 먼저 등호 오른쪽의 표현식이 평가되고 (0 제공) x가 x라고 생각합니다. x를 1로 증분하고 마지막으로 할당을 수행합니다 (x = 0을 다시 한 번 부여).
Tim Goodman

39

나는이 파티에 조금 늦었지만 최근에 나를 물린 두 가지 문제가 있습니다.

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\

17
~ 15ms 간격으로 시간을 양자화하는 것은 기본 타이밍 메커니즘의 정확도 부족으로 인한 것이 아닙니다 (이는 이전에 자세히 설명하지 않았습니다). 앱이 멀티 태스킹 OS 내에서 실행되고 있기 때문입니다. Windows는 15ms 정도마다 앱을 체크인하며, 시간이 조금 걸리는 동안 앱은 마지막 슬라이스 이후 대기 한 모든 메시지를 처리합니다. 해당 슬라이스 내 모든 통화는 정확히 같은 시간에 이루어지기 때문에 정확히 같은 시간에 전화를 겁니다.
MusiGenesis

2
@ MusiGenesis : (현재) 작동 방식을 알고 있지만 실제로는 정확하지 않은 정확한 측정 방법을 사용하는 것이 오도 된 것처럼 보입니다. 그것은 실제로 가장 가까운 천만으로 반올림 할 때 나노 미터의 높이를 알고 있다고 말하는 것과 같습니다.
Damovisa

7
DateTime은 단일 틱까지 저장할 수 있습니다. DateTime입니다.이 정확도를 사용하지 않습니다.
Ruben

16
여분의 '\'는 많은 유닉스 / 맥 / 리눅스 사람들에게 꼭 필요한 도구입니다. Windows에서 선행하는 '\'가 있으면 드라이브의 루트 (예 : C :)를 CD명령 에서 시도하여 의미하는 바를 확인하십시오. 1) Goto C:\Windows\System322) Type CD \Users3) Woah! 지금 당신은 C:\Users... GOT IT? ... Path.Combine (@ "C : \ Windows \ System32", @ "\ Users")는 \Users정확히 다음을 반환해야합니다 .[current_drive_here]:\Users
chakrit

8
'수면'이 없어도 동일한 방식으로 수행됩니다. 이것은 15ms마다 예약되는 앱과 관련이 없습니다. DateTime.UtcNow, GetSystemTimeAsFileTime에서 호출 한 기본 함수의 해상도가 좋지 않은 것 같습니다.
Jimbo

38

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


3
stdout 및 stderr을 모두 리디렉션하고 두 개의 ReadToEnd 호출을 순서대로 사용하는 경우에도 마찬가지입니다. stdout 및 stderr을 안전하게 처리하려면 각각에 대해 읽기 스레드를 작성해야합니다.
Sebastiaan M

34

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

여기를 참조 하십시오 .

간단히 말해서 Linq-To-Sql 쿼리의 조건절 내에서 null 참조 예외 ||와 같은 조건부 바로 가기를 사용할 수 없습니다 &&. Linq-To-Sql은 첫 번째 조건이 두 번째 조건을 평가할 필요가 없어도 OR 또는 AND 연산자의 양쪽을 평가합니다!


8
틸. BRB, 수백 개의 LINQ 쿼리를 다시 최적화 ...
tsilb

30

가상 메소드와 함께 기본 매개 변수 사용

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

산출 :
파생 된 기초


10
이상하게도 나는 이것이 완전히 명백하다고 생각했다. 선언 된 유형이 Base이면 컴파일러는 그렇지 않은 경우 기본값을 어디에서 가져와야 Base합니까? 나는 그것이 조금의 생각 것 보다 잡았다 선언 된 유형이있는 경우 기본 값이 다를 수 있음을 유도 (정적)라는 메소드가 기본 방법 임에도 불구하고, 유형입니다.
Timwi

1
메소드의 한 구현이 다른 구현의 기본값을 얻는 이유는 무엇입니까?
staafl

1
@staafl 기본 인수는 런타임이 아니라 컴파일 타임에 해결됩니다.
fredoverflow

1
나는이 gotcha가 일반적으로 기본 매개 변수라고 말하고 싶습니다. 사람들은 종종 런타임보다는 컴파일 타임에 해결되었음을 알지 못합니다.
Luaan

4
@FredOverflow, 내 질문은 개념적이었습니다. 동작은 구현에 의미가 있지만 직관적이지 않고 오류의 원인이 될 수 있습니다. IMHO C # 컴파일러는 재정의 할 때 기본 매개 변수 값을 변경할 수 없어야합니다.
staafl

27

변경 가능한 컬렉션의 값 개체

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

6
List <Point>와 같은 다른 동적 컬렉션과는 달리 (답과 반대되는) 배열과 실제로 작동한다는 점을 고려하면 왜 혼란 스럽습니까?
Lasse V. Karlsen

2
네가 옳아. 감사. 나는 내 대답을 고쳤다 :). arr[i].attr=라이브러리 컨테이너에서 코딩 할 수없는 배열에 대한 특수 구문입니다; (. (<value expression>). attr = <expr>이 전혀 허용되지 않는 이유는 무엇입니까?
Bjarke Ebert

1
@Bjarke Ebert : 이해가되는 경우가 있지만 안타깝게도 컴파일러가이를 식별하고 허용 할 방법이 없습니다. 샘플 사용 시나리오 : "회전 / 플립"표시기와 함께 2 차원 정사각형 배열에 대한 참조를 보유하는 불변의 Struct. 구조체 자체는 변경할 수 없으므로 읽기 전용 인스턴스의 요소에 쓰는 것이 좋지만 컴파일러는 속성 설정자가 실제로 구조체를 쓰지 않을 것이므로 알 수 없습니다. .
supercat

25

아마도 최악은 아니지만 .net 프레임 워크의 일부 는 학위사용 하는 반면 다른 일부는 라디안을 사용합니다 (Intellisense와 함께 제공되는 문서는 MSDN을 방문하여 찾을 필요가 없음)

Angle대신 에 수업을 함으로써이 모든 것을 피할 수있었습니다 ...


나는이 내 다른 개는이보다 훨씬 더 나쁘다 고려, 많은 upvotes있어 놀랐어요
BlueRaja - 대니 Pflughoeft

22

C / C ++ 프로그래머에게는 C #으로의 전환이 당연합니다. 그러나 개인적으로 (그리고 다른 사람들과 같은 전환을 보았던) 가장 큰 어려움은 C #의 클래스와 구조체의 차이점을 완전히 이해하지 못한다는 것입니다.

C ++에서 클래스와 구조체는 동일합니다. 클래스는 기본적으로 비공개 가시성을 기본으로하고 기본적으로 공개 가시성을 기본으로하는 기본 가시성 만 다릅니다. C ++에서이 클래스 정의

    class A
    {
    public:
        int i;
    };

이 구조체 정의와 기능적으로 같습니다.

    struct A
    {
        int i;
    };

그러나 C #에서 클래스는 참조 형식이고 구조체는 값 형식입니다. 이것은 (1) 다른 것을 사용할 때를 결정하는 것, (2) 물체의 동등성을 테스트하는 것, (3) 성능 (예 : 복싱 / 언 박싱) 등에서 차이를 만듭니다 .

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


13
그래서 최악의 문제는 사람들이 언어를 사용하기 전에 시간을내어 귀찮게하지 않습니까?
BlueRaja-대니 Pflughoeft

3
@ BlueRaja-DannyPflughoeft 분명히 비슷한 언어의 고전적인 방식과 비슷합니다. 비슷한 키워드를 사용하고 많은 경우 구문을 사용하지만 다른 방식으로 작동합니다.
Luaan

19

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


2
using () 블록은이 문제를 깔끔하게 해결합니다. Dispose에 대한 호출을 볼 때마다 using ()을 사용하기 위해 즉시 안전하게 리팩터링 할 수 있습니다.
Jeremy Frey

5
나는 우려가 IDisposable을 올바르게 구현 하고 있다고 생각 합니다.
Mark Brackett

4
반면, using () 습관은 PInvoke로 작업 할 때처럼 예기치 않게 물릴 수 있습니다. API가 여전히 참조하고있는 것을 폐기하고 싶지 않습니다.
MusiGenesis

3
IDisposable을 올바르게 구현하는 것은 매우 어렵고 여기에서 찾은 최고의 조언 (.NET Framework 지침)조차도 최종적으로 "얻을"때까지 적용하기가 어려울 수 있습니다.
Quibblesome

1
IDisposable에서 찾은 최고의 조언은 Stephen Cleary가 제공하는 세 가지 쉬운 규칙IDisposable에 대한 심층 기사
Roman Starkov

19

배열 구현 IList

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

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

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

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


8
IMHO, 문제는 Microsoft에 컬렉션에 대해 정의 된 인터페이스가 충분하지 않다는 것입니다. IMHO에는 iEnumerable, iMultipassEnumerable (재설정을 지원하고 여러 패스가 일치하도록 보장), iLiveEnumerable (열거 중에 컬렉션이 변경되면 의미가 부분적으로 정의되어 있어야 함)-변경 사항이 열거에 나타날 수도 있고 나타나지 않을 수도 있음 가짜 결과 또는 예외), iReadIndexable, iReadWriteIndexable 등. 인터페이스가 다른 인터페이스를 "상속"할 수 있기 때문에 추가 작업이없는 경우에는 추가 작업이 추가되지 않습니다 (NotImplemented 스텁 저장).
supercat

@ supercat, 초보자와 특정 장시간 코더에게는 혼란 스러울 것입니다. .NET 컬렉션과 해당 인터페이스가 훌륭하다고 생각합니다. 하지만 당신의 겸손에 감사드립니다. ;)
Jordan

@Jordan : 위의 내용을 작성한 이후로, 더 나은 접근 방식은 속성 을 보유 IEnumerable<T>하고 IEnumerator<T>지원하는 것이 Features아니라 "기능"이보고 한 내용에 따라 유용성이 결정되는 "선택적"방법을 결정하기로 결정했습니다. 그러나 나는 요점을 받아들이는 IEnumerable<T>것이 코드를 제공하는 것보다 더 강력한 약속이 필요한 경우가 있다는 것 IEnumerable<T>입니다. 호출 ToList수율 것 IEnumerable<T>같은 약속을 붙들고 그,하지만 것은 많은 경우에 불필요하게 비싼 것입니다. 나는 있어야한다고 주장 할 것이다 ...
supercat

... 필요한 경우 코드를받는 코드 IEnumerable<T>가 내용의 사본을 만들 수 있지만 불필요하게 수행하지 않을 수 있는 수단 입니다.
supercat

귀하의 옵션은 절대 읽을 수 없습니다. 코드에서 IList를 볼 때 Features 속성을 조사하지 않고 작업하고있는 것을 알고 있습니다. 프로그래머는 코드의 중요한 기능이 컴퓨터뿐만 아니라 사람들이 읽을 수 있다는 점을 잊고 싶어합니다. .NET 컬렉션 네임 스페이스는 이상적이지 않지만 좋지만 때로는 최상의 솔루션을 찾는 것이 원칙을보다 이상적으로 맞추는 것이 아닙니다. 내가 함께 작업 한 최악의 코드 중 일부는 DRY에 이상적으로 적합한 코드였습니다. 나는 그것을 폐기하고 다시 썼다. 그냥 나쁜 코드였습니다. 나는 당신의 프레임 워크를 전혀 사용하고 싶지 않습니다.
Jordan

18

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"5 개를 인쇄하지만 다음 예제는 제대로 작동합니다.

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
이것은 익명 메소드를 사용한 Jon의 예제와 본질적으로 동일합니다.
Mehrdad Afshari

3
"s"변수가 범위 변수와 더 쉽게 혼합되는 foreach와 훨씬 더 혼동됩니다. 일반적인 for-loops에서 인덱스 변수는 각 반복마다 동일합니다.
Mikko Rantanen

2
blogs.msdn.com/ericlippert/archive/2009/11/12/… 그리고 네, 변수의 범위가 "적절하게"되었으면합니다.
Roman Starkov


본질적으로 동일한 변수를 변경하지 않고 반복해서 인쇄하는 것입니다.
Jordan

18

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


17
솔직히 MS SQL Server 에이 권리가 있고 .Net이 잘못되었다고 생각합니다. 조사를하면 달력 변경, 날짜 건너 뛰기 등으로 인해 1751 년 이전 날짜가 펑키하다는 것을 알 수 있습니다. 대부분의 RDBM에는 약간의 차이가 있습니다. 이것은 당신에게 시작점을 제공해야합니다 : ancestry.com/learn/library/article.aspx?article=3358
NotMe에게

11
또한 날짜는 1753입니다. 날짜를 건너 뛰지 않고 연속 일정이있는 것은 처음이었습니다. SQL 2008은 1/1/01에서 12/31/9999까지의 날짜를 수용 할 수있는 Date 및 datetime2 날짜 유형을 도입했습니다. 그러나 1753 년 이전의 날짜를 실제로 비교하는 경우 해당 유형을 사용한 날짜 비교를 의심스럽게 봐야합니다.
NotMe

아, 1753 년, 고마워요.
Shaul Behr

그러한 날짜와 날짜를 비교하는 것이 실제로 의미가 있습니까? 히스토리 채널의 경우 이것은 의미가 있지만 미국이 발견 된 정확한 요일을 알고 싶지는 않습니다.
Camilo Martin

5
Julian Day의 Wikipedia를 통해 1984 년에 출판 된 13 라인 기본 프로그램 CALJD.BAS를 찾을 수 있습니다.이 날짜는 1753 년에 윤년과 건너 뛴 날을 고려하여 기원전 5000 년으로 거슬러 올라갑니다. "SQL2008과 같은 시스템은 더 나빠질 것입니다. 15 세기의 정확한 날짜 표현에 관심이 없을 수도 있지만 다른 사람들도있을 수 있으며 소프트웨어는 버그없이이를 처리해야합니다. 또 다른 문제는 윤초입니다. . .
Roland

18

불쾌한 Linq 캐싱 잡기

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

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

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

고차!


IV는 단지이 버그를 쫓아 하루 반 (그리고 많은 머리!) 보냈다 ...
외과 코더

이것은 동시성 충돌이라고 불리우며, 비록 약간 무겁지만 경향이 있지만 지금은 특정한 방법이 있지만 오늘날에도 여전히 문제가되고 있습니다. DataContext는 악몽이었습니다. O_o
Jordan

17

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

1
또는 명시적인 예에서 : var r = new BinaryReader(stream); ulong data = r.ReadUInt64();. BinaryReader에는 FillBuffer방법도 있습니다.
jimbobmcgee

15

행사

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


1
또한 멀티 스레딩은 고통 스럽습니다. 이러한 모든 문제를 제외하고는 아무것도 CAB에서 수정되었습니다 (이 기능은 실제로 언어로 작성되어야 함). 이벤트는 전역 적으로 선언되며 모든 메소드는 이벤트의 "구독자"라고 선언 할 수 있습니다. CAB의 유일한 문제는 전역 이벤트 이름이 열거 형이 아닌 문자열이라는 것입니다 (Java와 같이보다 지능적인 열거 형으로 본질적으로 문자열로 작동합니다!) . CAB을 설정하기는 어렵지만 여기 에서 간단한 오픈 소스 복제본을 사용할 수 있습니다 .
BlueRaja-대니 Pflughoeft

3
.net 이벤트 구현이 마음에 들지 않습니다. 이벤트 구독은 구독을 추가하고 Dispose'd가 구독을 삭제하는 IDisposable을 반환하는 메서드를 호출하여 처리해야합니다. 시맨틱이 다소 피할 수있는 "add"및 "remove"메소드를 결합하는 특수한 구성이 필요하지 않습니다. 특히 멀티 캐스트 대리자를 추가하고 나중에 제거하려고 시도하는 경우 (예 : "B"뒤에 "AB"를 추가 한 다음 제거) "B"( "BA"떠나기) 및 "AB"(여전히 "BA"떠나기)
supercat

@supercat 어떻게 다시 쓰 button.Click += (s, e) => { Console.WriteLine(s); }시겠습니까?
Ark-kun

나는 다른 이벤트와는 별도로 가입을 취소 할 수,해야 할 경우 IEventSubscription clickSubscription = button.SubscribeClick((s,e)=>{Console.WriteLine(s);});와를 통해 구독 취소 clickSubscription.Dispose();. 내 객체가 수명 기간 동안 모든 구독을 유지 MySubscriptions.Add(button.SubscribeClick((s,e)=>{Console.WriteLine(s);}));한 다음 MySubscriptions.Dispose()모든 구독을 종료하려면.
supercat

@ Ark-kun : 구독 외부를 캡슐화하는 객체를 유지 해야하는 것은 성가신 것처럼 보일 수 있지만 엔티티로 구독을 고려하면 모든 것을 정리할 수있는 유형으로 집계 할 수 있습니다. 그렇지 않으면 매우 어려운 일입니다.
supercat

14

오늘 나는 오랫동안 뛰어 다니지 않는 버그를 수정했습니다. 버그는 멀티 스레드 시나리오에서 사용되는 일반 클래스에 있었고 정적 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


5
정적을 정의하고 제네릭을 상속하는 제네릭이 아닌 기본 클래스를 가질 수 있습니다. 비록 C #에서이 동작에 빠지지는 않았지만 C ++ 템플릿의 오랜 디버깅 시간을 기억합니다. :)
Paulius

7
이상하게도 나는 이것이 명백하다고 생각했다. i유형이있는 경우 어떻게해야하는지 생각해보십시오 T.
Timwi

1
type 매개 변수는의 일부입니다 Type. SomeGeneric<int>와 다른 유형입니다 SomeGeneric<string>. 물론 각기 고유의public static int i
radarbob

13

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

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

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

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


이것은 의도적으로 설계된 동작입니다. 이것을 지연된 실행이라고합니다. 무엇보다도 TSQL 구문을 시뮬레이션하기위한 것입니다. SQL보기에서 선택할 때마다 다른 결과가 나타납니다. 또한 SQL Server와 같은 원격 데이터 저장소에 유용한 체인을 허용합니다. 그렇지 않으면 x.Select.Where.OrderBy는 데이터베이스에 3 개의 개별 명령을 보냅니다.
as9876

@AYS 질문 제목에서 "Gotcha"라는 단어를 놓치셨습니까?
chakrit

나는 의도적 인 것이 아니라 디자이너의 감독을 의미한다고 생각했다.
as9876

다시 시작할 수없는 IEnumerable에 대한 다른 유형이있을 수 있습니다. AutoBufferedEnumerable? 쉽게 구현할 수 있습니다. 이 문제는 프로그래머의 지식 부족으로 인한 것 같습니다. 현재 동작에 문제가 있다고 생각하지 않습니다.
Eldritch Conundrum

13

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

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

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

이것이 C #이 널 입력 가능 유형에 대한 조작을 활용하는 방법의 결과입니다. 그것은 당신이 던지는 모든 것을 소비하는 NaN과 약간 비슷합니다.
IllidanS4는 Monica를

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

그렇습니다.이 동작은 문서화되었지만 확실하게 올바른 것은 아닙니다.


5
동의하지 않습니다. 단어가 모두 대문자 인 경우 제목 상자를 엉망으로 만들고 싶지 않다는 특별한 의미를 가질 수 있습니다 (예 : "미국 대통령"-> "미국 대통령"). 미국".
Shaul Behr

5
@Shaul는 : 인 경우, 그들은으로 이것을 지정해야합니다 매개 변수 - 이것을 만드는 나는 시간에 앞서이 문제가 예상되는 사람이 만난 적이 있기 때문에, 회피 혼란을 잡았다을 !
BlueRaja-대니 Pflughoeft
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.