C # 또는 .NET에서 가장 이상한 경우는 무엇입니까? [닫은]


322

나는 몇 가지 코너 케이스와 뇌 맛보기를 수집하고 항상 더 많은 것을 듣고 싶습니다. 이 페이지는 실제로 C # 언어 비트와 밥을 다루지 만 핵심 .NET 항목도 흥미 롭습니다. 예를 들어, 여기 페이지에 없지만 믿을 수없는 것을 발견했습니다.

string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y));

False를 인쇄 할 것으로 예상됩니다-결국 "새"(참조 유형) 항상 새 개체를 생성합니까? C #과 CLI 모두에 대한 사양에 따라야합니다. 글쎄,이 특별한 경우에는 그렇지 않습니다. True를 인쇄하고 테스트 한 모든 버전의 프레임 워크에서 수행했습니다. (필자는 분명히 모노에서 시도하지 않았습니다 ...)

분명히, 이것은 내가 찾고있는 종류의 예일뿐입니다-나는이 이상한 점에 대한 토론 / 설명을 특별히 찾지 않았습니다. (일반 문자열 인턴과 동일하지 않습니다. 특히 문자열 인턴은 생성자가 호출 될 때 일반적으로 발생하지 않습니다.) 실제로 비슷한 이상한 동작을 요구했습니다.

다른 보석이 숨어 있습니까?


64
Mono 2.0 rc에서 테스트되었습니다. True를 반환
Marc Gravell

10
두 문자열은 모두 문자열로 끝납니다.
비어

34
메모리 절약입니다. 정적 메소드 문자열에 대한 MSDN 문서를 찾으십시오. CLR은 문자열 풀을 유지 관리합니다. 이것이 동일한 내용의 문자열이 동일한 메모리 즉 객체에 대한 참조로 나타나는 이유입니다.
John Leidegren

12
@John : 문자열 인터 닝은 리터럴에 대해서만 자동으로 발생합니다 . 여기서는 그렇지 않습니다. @DanielSwe : 문자열을 불변으로 만드는 인터 닝이 필요 하지 않습니다 . 그것이 가능하다는 사실은 불변성의 훌륭한 추론이지만, 정상적인 인턴은 여기서 일어나지 않습니다.
Jon Skeet

3
이 동작의 원인이되는 구현 세부 사항은 여기에 설명되어 있습니다. blog.liranchen.com/2010/08/brain-teasing-with-strings.html
Liran

답변:


394

나는 당신에게 이것을 이전에 보여줬다 고 생각하지만, 나는 여기서 재미를 좋아합니다-이것은 추적하기 위해 약간의 디버깅이 필요했습니다! (원래 코드는 분명히 더 복잡하고 미묘했습니다 ...)

    static void Foo<T>() where T : new()
    {
        T t = new T();
        Console.WriteLine(t.ToString()); // works fine
        Console.WriteLine(t.GetHashCode()); // works fine
        Console.WriteLine(t.Equals(t)); // works fine

        // so it looks like an object and smells like an object...

        // but this throws a NullReferenceException...
        Console.WriteLine(t.GetType());
    }

그래서 T는 ...

답변 : 어떤 Nullable<T>- 등 int?. GetType ()을 제외한 모든 메서드는 재정의됩니다. 따라서 null을 호출하는 object.GetType () ...을 호출하기 위해 객체로 캐스팅 (따라서 null로)됩니다.


업데이트 : 줄거리가 두껍게 ... Ayende Rahien은 자신의 블로그 에서 비슷한 도전을 던졌지 만 where T : class, new():

private static void Main() {
    CanThisHappen<MyFunnyType>();
}

public static void CanThisHappen<T>() where T : class, new() {
    var instance = new T(); // new() on a ref-type; should be non-null, then
    Debug.Assert(instance != null, "How did we break the CLR?");
}

그러나 이길 수 있습니다! 원격과 같은 것들에 의해 사용 된 것과 같은 간접적 사용; 경고-다음은 순수한 악입니다 .

class MyFunnyProxyAttribute : ProxyAttribute {
    public override MarshalByRefObject CreateInstance(Type serverType) {
        return null;
    }
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }

이를 사용하면 new()호출이 프록시 ( MyFunnyProxyAttribute)로 리디렉션되어을 반환합니다 null. 자 가서 눈을 씻으십시오!


9
Nullable <T> .GetType ()을 정의 할 수없는 이유는 무엇입니까? 결과가 typeof (Nullable <T>)가 아니어야합니까?
Drew Noakes

69
Drew : 문제는 GetType ()이 가상이 아니므로 재정의되지 않는다는 것입니다. 즉, 메서드 호출을 위해 값이 박스로 표시됩니다. 상자는 Null 참조가되므로 NRE가됩니다.
Jon Skeet

10
@ 드류; 또한 Nullable <T>에 대한 특수한 권투 규칙이 있습니다. 즉, 빈 Nullable <T>가 포함 된 상자가 아니라 빈 Nullable <T> 상자가 null로 설정되고 빈 Nullable <T로 상자가 없으면 null이 해제됩니다. >)
Marc Gravell

29
아주 멋져요 냉정한 방법으로. ;-)
Konrad Rudolph

6
C # 3.0 언어 사양에서 생성자 제약 조건, 10.1.5
Marc Gravell

216

은행가의 반올림.

이것은 컴파일러 버그 나 오작동이 아니라 이상한 코너 케이스입니다 ...

.Net Framework는 Banker 's Rounding으로 알려진 구성표 또는 반올림을 사용합니다.

은행원 반올림에서 0.5 숫자는 가장 가까운 짝수로 반올림되므로

Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...

이로 인해 잘 알려진 Round-Half-Up 반올림을 기반으로 재무 계산에서 예기치 않은 버그가 발생할 수 있습니다.

Visual Basic에서도 마찬가지입니다.


22
나에게도 이상해 보였다. 그것은 적어도 큰 숫자 목록을 모아서 합계를 계산할 때까지입니다. 그런 다음 단순히 반올림하면 반올림되지 않은 숫자의 합계와 잠재적으로 큰 차이가 생길 수 있습니다. 재무 계산을 수행하는 경우 매우 나쁩니다!
Tsvetomir Tsonev

255
사람들이 모르는 경우 다음을 수행 할 수 있습니다. Math.Round (x, MidpointRounding.AwayFromZero); 반올림 구성표를 변경합니다.
ICR

26
문서에서 :이 방법의 동작은 IEEE 표준 754, 섹션 4를 따릅니다. 이러한 종류의 반올림은 때때로 가장 가까운 반올림 또는 은행가의 반올림이라고합니다. 중간 지점 값을 한 방향으로 일관되게 반올림하여 발생하는 반올림 오류를 최소화합니다.
ICR

8
이것이 int(fVal + 0.5)내장 된 반올림 기능이있는 언어에서도 자주 볼 수있는 이유인지 궁금 합니다.
Ben Blank

32
아이러니하게도, 나는에서 근무 은행 번과 다른 프로그래머는 반올림이 프레임 워크에 부서졌다 생각이에 대해 알아 뒤집기 시작

176

이 함수는 Rec(0)(디버거가 아닌)으로 호출되면 어떻게됩니까?

static void Rec(int i)
{
    Console.WriteLine(i);
    if (i < int.MaxValue)
    {
        Rec(i + 1);
    }
}

대답:

  • 32 비트 JIT에서는 StackOverflowException이 발생합니다.
  • 64 비트 JIT에서는 모든 숫자를 int.MaxValue에 인쇄해야합니다.

때문입니다 64 비트 JIT 컴파일러는 꼬리 호출 최적화를 적용 32 비트 JIT하지 않는 반면,.

불행히도 이것을 검증 할 64 비트 머신이 없지만 메소드는 테일 콜 최적화를위한 모든 조건을 충족합니다. 아무도 가지고 있지 않다면 나는 그것이 사실인지 알고 싶습니다.


10
릴리스 모드에서 컴파일하지만, 가장 확실히) 64 = 작동되어야한다
닐 윌리엄스에게

3
VS 2010 나오면 다음 TCO 릴리스 모드에서 할 것입니다 현재의 모든 JITs 때문에 답을 업데이트 할 가치가있을 수도 있습니다
ShuggyCoUk

3
32 비트 WinXP에서 VS2010 Beta 1을 사용해 보았습니다. 여전히 StackOverflowException이 발생합니다.
squillman

130
StackOverflowException +1
calvinlough

7
저를 ++완전히 버렸습니다. Rec(i + 1)평범한 사람처럼 전화 할 수 없습니까 ?
구성자

111

이것을 할당하십시오!


이것은 내가 파티에서 묻고 싶은 것입니다 (아마도 더 이상 초대받지 않는 이유 일 것입니다).

다음 코드를 컴파일 할 수 있습니까?

    public void Foo()
    {
        this = new Teaser();
    }

쉬운 속임수는 다음과 같습니다.

string cheat = @"
    public void Foo()
    {
        this = new Teaser();
    }
";

그러나 실제 해결책은 다음과 같습니다.

public struct Teaser
{
    public void Foo()
    {
        this = new Teaser();
    }
}

따라서 값 유형 (구조체)이 this변수를 재 할당 할 수 있다는 사실은 알지 못합니다 .


3
C ++ 클래스도 그렇게 할 수있다. 내가 최근에 발견 한 것처럼 실제로 최적화를 위해 그것을 사용하려고 시도한 것에 대해서만 소리 쳤다. : p
mpen

1
나는 실제로 새로운 곳을 사용하고있었습니다. 모든 필드를 업데이트하는 효율적인 방법을 원했습니다. :)
mpen

70
이것은 또한 속임수입니다 : //this = new Teaser();:-)
AndrewJacksonZA

17
:-)이 재배치 혐오보다 내 프로덕션 코드에서 이러한 치트를 선호합니다.
Omer Mor

2
CLR에서 C #을 통해 : 그들이 만든 이유는 다른 생성자에서 구조체의 매개 변수없는 생성자를 호출 할 수 있기 때문입니다. 구조체의 한 값만 초기화하고 다른 값은 0 / 널 (기본값)이되도록하려면을 쓸 수 있습니다 public Foo(int bar){this = new Foo(); specialVar = bar;}. 이것은 효율적이지 않고 실제로 정당화되지는 않지만 ( specialVar두 번 할당 됨) 단지 FYI입니다. (이것이 책에 주어진 이유이기 때문에 우리가 왜 그렇게해서는 public Foo(int bar) : this()
안되는지 모르겠습니다

100

몇 년 전, 충성도 프로그램을 진행할 때 고객에게 제공되는 포인트의 양에 문제가있었습니다. 이 문제는 double을 int로 캐스팅 / 변환하는 것과 관련이 있습니다.

아래 코드에서 :

double d = 13.6;

int i1 = Convert.ToInt32(d);
int i2 = (int)d;

i1 == i2 입니까?

i1! = i2로 밝혀졌습니다. 변환 및 캐스트 연산자에서 다른 반올림 정책으로 인해 실제 값은 다음과 같습니다.

i1 == 14
i2 == 13

항상 Math.Ceiling () 또는 Math.Floor () 또는 요구 사항을 충족하는 MidpointRounding을 사용하는 Math.Round를 호출하는 것이 좋습니다.

int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);

44
정수로 캐스팅하면 반올림되지 않고 잘립니다 (항상 항상 반올림). 그래서 이것은 완전한 의미가 있습니다.
Max Schmeling

57
@ 최대 : 예, 그러나 왜 변환이 라운드됩니까?
Stefan Steinegger

18
@Stefan Steinegger 만약 모든 것이 던져 졌다면, 그 이유는 없었을까요? 또한 클래스 이름은 Cast not Cast입니다.
bug-a-lot

3
VB에서 : CInt () 반올림. Fix ()가 잘립니다. 한 번 불 태워 ( blog.wassupy.com/2006/01/i-can-believe-it-not-truncating.html )
Michael Haren

74

열거 형 함수 과부하가 있어도 0을 정수로 만들어야합니다.

0을 열거 형에 매핑하는 C # 핵심 팀의 이론적 근거를 알고 있었지만 여전히 직교하지는 않습니다. Npgsql의 .

테스트 예 :

namespace Craft
{
    enum Symbol { Alpha = 1, Beta = 2, Gamma = 3, Delta = 4 };


   class Mate
    {
        static void Main(string[] args)
        {

            JustTest(Symbol.Alpha); // enum
            JustTest(0); // why enum
            JustTest((int)0); // why still enum

            int i = 0;

            JustTest(Convert.ToInt32(0)); // have to use Convert.ToInt32 to convince the compiler to make the call site use the object version

            JustTest(i); // it's ok from down here and below
            JustTest(1);
            JustTest("string");
            JustTest(Guid.NewGuid());
            JustTest(new DataTable());

            Console.ReadLine();
        }

        static void JustTest(Symbol a)
        {
            Console.WriteLine("Enum");
        }

        static void JustTest(object o)
        {
            Console.WriteLine("Object");
        }
    }
}

18
와우, 그것은 저에게 새로운 것입니다. 또한 ConverTo.ToIn32 ()는 작동하지만 (int) 0으로 캐스팅하는 방법은 없습니다. 그리고 다른 숫자> 0이 작동합니다. ( "작동"으로 개체 오버로드를 의미합니다.)
Lucas

이 동작에 대한 모범 사례를 적용하기위한 권장 코드 분석 규칙이 있습니다. msdn.microsoft.com/en-us/library/ms182149%28VS.80%29.aspx 이 링크에는 0 매핑의 작동 방식에 대한 훌륭한 설명도 포함되어 있습니다. .
Chris Clark

1
@ Chris Clark : 열거 형 기호에 None = 0을 넣으려고했습니다. 여전히 컴파일러이 선택하는의 0 열거 심지어 (INT) 0
마이클 부엔

2
IMO는 none어떤 열거 형으로 변환하여 사용할 수 있는 키워드 를 도입해야하며 0을 항상 int로 만들고 암묵적으로 열거 형으로 변환 할 수는 없습니다.
코드 InChaos

5
결과는 컴파일 타임 상수가 아니기 때문에 ConverTo.ToIn32 ()가 작동합니다. 그리고 컴파일 타임 상수 0 만 열거 형으로 변환 할 수 있습니다. 이전 버전의 .net에서는 리터럴 만 0열거 형으로 변환 할 수있었습니다. Eric Lippert의 블로그를 참조하십시오 : blogs.msdn.com/b/ericlippert/archive/2006/03/28/563282.aspx
CodesInChaos

67

이것은 내가 지금까지 본 것 중 가장 특이한 것 중 하나입니다 (물론 여기에서 제외하십시오!) :

public class Turtle<T> where T : Turtle<T>
{
}

그것은 당신이 그것을 선언 할 수는 있지만 실제 사용하지는 않습니다. 왜냐하면 그것은 항상 당신이 다른 클래스로 센터에있는 클래스를 포장하도록 요구하기 때문입니다.

[농담] 거북이가 끝까지 내려간 것 같아 ... [/ 농담]


34
그러나 인스턴스를 만들 수 있습니다.class RealTurtle : Turtle<RealTurtle> { } RealTurtle t = new RealTurtle();
Marc Gravell

24
과연. 이것은 Java 열거 형이 큰 효과를 내기 위해 사용하는 패턴입니다. 프로토콜 버퍼에서도 사용합니다.
Jon Skeet

6
RCIX, 그렇습니다.
여호수아

8
나는이 패턴을 멋진 제네릭 물건에 많이 사용했습니다. 올바르게 입력 된 복제 또는 자체 인스턴스 작성과 같은 항목을 허용합니다.
Lucero

20
이것은 '
기발

65

최근에 알게 된 것 중 하나입니다 ...

interface IFoo
{
   string Message {get;}
}
...
IFoo obj = new IFoo("abc");
Console.WriteLine(obj.Message);

위의 첫눈에 미친 보이지만은 실제로 법적 내가 핵심을 놓친 적이 있지만 (정말, - 안돼, 그러나 그것은 아니다 "라는 클래스를 추가처럼 아무것도 해키 IFoo"또는 "는 추가 using점에 별칭을 IFoo에서 수업").

이유를 알아낼 수 있는지 확인하십시오. 누가 인터페이스를 인스턴스화 할 수 없다고 말합니까?


1
"사용하여 별칭"에 대한 한 - 난 당신이 할 수있는 알지도 못하는 것을 !
David

COM Interop의 컴파일러에서 해킹 :-)
Ion Todirel

놈아! 적어도 "특정 상황에서"라고 말했을 수 있습니다 ... 내 컴파일러가 반대합니다!
MA Hanin

56

부울이 True 또는 False가 아닌 경우는 언제입니까?

Bill은 부울을 해킹하여 A가 True이고 B가 True 인 경우 (A 및 B)가 False임을 발견했습니다.

해킹 된 부울


134
물론 FILE_NOT_FOUND 일 때!
Greg Greg

12
수학적으로 말하면 C #의 문은 입증 할 수 없기 때문에 이것은 흥미 롭습니다. 죄송합니다.
Simon Johnson

20
언젠가 나는이 행동에 의존하는 프로그램을 작성하고 가장 어두운 지옥의 악마들이 나를 환영 할 준비를 할 것입니다. Bwahahahahaha!
Jeffrey L Whitledge

18
이 예제는 논리 연산자가 아닌 비트 단위를 사용합니다. 어떻게 놀랍습니까?
Josh Lee

6
글쎄, 그는 구조체의 레이아웃을 해킹합니다. 물론 이상한 결과를 얻을 것입니다. 놀랐거나 예기치 않은 것은 아닙니다!
Ion Todirel

47

나는 파티에 조금 늦게 도착 해요,하지만 난있어 다섯 :

  1. 로드 / 표시되지 않은 컨트롤에서 InvokeRequired를 폴링하면 false로 표시되고 다른 스레드에서 변경하려고하면 얼굴이 날려 버립니다 ( 해결 방법 은 이것을 참조하는 것입니다). 제어).

  2. 나를 놀라게 한 또 다른 하나는 다음과 같은 어셈블리가 주어진 것입니다.

    enum MyEnum
    {
        Red,
        Blue,
    }

    다른 어셈블리에서 MyEnum.Red.ToString ()을 계산하고 때때로 누군가가 열거 형을 다음과 같이 컴파일했습니다.

    enum MyEnum
    {
        Black,
        Red,
        Blue,
    }

    런타임에 "검정색"이 표시됩니다.

  3. 나는 편리한 상수를 가진 공유 어셈블리를 가지고 있었다. 나의 전임자는 추악하게 보이는 get-only 속성을 남겨두고 혼란을 없애고 public const를 사용한다고 생각했다. VS가 참조가 아닌 값으로 컴파일했을 때 조금 놀랐습니다.

  4. 다른 어셈블리에서 인터페이스의 새 메소드를 구현하지만 해당 어셈블리의 이전 버전을 참조하여 다시 빌드하면 구현 한 경우에도 TypeLoadException ( 'NewMethod'구현 아님) 이 발생 합니다 ( 여기 참조 ).

  5. Dictionary <,> : "항목이 반환되는 순서는 정의되어 있지 않습니다". 이것은 때때로 당신을 물 수 있지만 다른 사람들을 일할 수 있기 때문에 끔찍 합니다. 사전이 훌륭하게 재생 될 것이라고 맹목적으로 가정한다면 ( "왜 안됩니까? 생각했습니다, List는 생각합니다"), 당신은 정말로해야합니다. 마침내 당신의 가정에 의문을 제기하기 전에 코를 넣으십시오.


6
# 2는 흥미로운 예입니다. 열거 형은 정수 값에 대한 컴파일러 매핑입니다. 따라서 명시 적으로 값을 할당하지 않더라도 컴파일러에서 MyEnum.Red = 0 및 MyEnum.Blue = 1이 발생했습니다. Black을 추가하면 값 0을 재정 의하여 Red에서 Black으로 매핑합니다. 직렬화와 같은 다른 사용법에서도 문제가 발생했을 것으로 생각됩니다.
LBushkin

3
호출에 +1이 필요합니다. 우리는 Red = 1, Blue = 2와 같은 열거 형에 값을 명시 적으로 할당하여 새 값이 항상 같은 값을 갖기 전후에 삽입하는 것을 선호합니다. 데이터베이스에 값을 저장하는 경우 특히 필요합니다.
TheVillageIdiot

53
# 5가 "가장 큰 경우"라는 의견에 동의하지 않습니다. 값을 삽입 할 때를 기준으로 사전에 정의 된 순서가 없어야합니다. 정의 된 순서를 원하면 List를 사용하거나 유용한 방식으로 정렬 할 수있는 키를 사용하거나 완전히 다른 데이터 구조를 사용하십시오.
웨지

21
@Wedge, SortedDictionary처럼?
Allon Guralnek

4
# 3 상수는 사용되는 모든 곳에서 리터럴로 삽입되기 때문에 발생합니다 (적어도 C #에서는). 당신의 전신은 이미 그것을 알아 차렸을 것입니다. 그래서 그들은 get-only 속성을 사용했습니다. 그러나 읽기 전용 변수 (const와 반대)도 마찬가지로 작동합니다.
Remoun

33

VB.NET, 널 입력 가능 및 삼항 연산자 :

Dim i As Integer? = If(True, Nothing, 5)

i포함하기를 기대했기 때문에 디버깅하는 데 시간이 걸렸습니다 Nothing.

내가 실제로 무엇을 포함합니까? 0.

이것은 놀라운 일이지만 실제로는 "올바른"동작입니다. NothingVB.NET의 nullCLR 과 정확히 동일하지는 않습니다 . 상황에 따라 또는 type type을 Nothing의미 할 수 있습니다 . 위의 경우, 추론한다 공통의 종류로 하고 있으므로,이 경우, 방법 .nulldefault(T)TIfIntegerNothing5Nothing0


충분히 흥미롭게도이 답변을 찾지 못해 질문 을 작성해야했습니다 . 글쎄, 누가이 스레드에 답을 알았습니까?
GSerg

28

장거리 촬영으로 첫 번째를 능가하는 두 번째 이상한 코너 케이스를 발견했습니다.

String.Equals 메서드 (String, String, StringComparison)는 실제로 부작용이 없습니다.

나는 일부 기능의 맨 위에 자체적으로 이것을 가지고있는 코드 블록을 연구하고 있었다.

stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);

해당 줄을 제거하면 프로그램의 다른 곳에서 스택 오버플로가 발생합니다.

이 코드는 본질적으로 BeforeAssemblyLoad 이벤트에 대한 핸들러를 설치하고 시도하는 것으로 밝혀졌습니다.

if (assemblyfilename.EndsWith("someparticular.dll", StringComparison.InvariantCultureIgnoreCase))
{
    assemblyfilename = "someparticular_modified.dll";
}

지금은 말할 필요가 없습니다. 문자열 비교에서 이전에 사용되지 않은 문화권을 사용하면 어셈블리로드가 발생합니다. InvariantCulture는 이에 대한 예외가 아닙니다.


BeforeAssemblyLoad로 볼 수 있기 때문에 "어셈블리로드"는 부작용 인 것 같습니다!
Jacob Krall

2
와. 이것은 관리자의 다리에 완벽하게 맞습니다. BeforeAssemblyLoad 핸들러를 작성하면 많은 놀라움을 초래할 수 있습니다.
wigy

20

다음은 "보호 된 메모리를 읽거나 쓰려고 시도했습니다. 이것은 종종 다른 메모리가 손상되었다는 표시"오류 메시지를 발생시키는 구조체를 만드는 방법의 예입니다. 성공과 실패의 차이는 매우 미묘합니다.

다음 단위 테스트는 문제를 보여줍니다.

무엇이 잘못되었는지 알아낼 수 있는지보십시오.

    [Test]
    public void Test()
    {
        var bar = new MyClass
        {
            Foo = 500
        };
        bar.Foo += 500;

        Assert.That(bar.Foo.Value.Amount, Is.EqualTo(1000));
    }

    private class MyClass
    {
        public MyStruct? Foo { get; set; }
    }

    private struct MyStruct
    {
        public decimal Amount { get; private set; }

        public MyStruct(decimal amount) : this()
        {
            Amount = amount;
        }

        public static MyStruct operator +(MyStruct x, MyStruct y)
        {
            return new MyStruct(x.Amount + y.Amount);
        }

        public static MyStruct operator +(MyStruct x, decimal y)
        {
            return new MyStruct(x.Amount + y);
        }

        public static implicit operator MyStruct(int value)
        {
            return new MyStruct(value);
        }

        public static implicit operator MyStruct(decimal value)
        {
            return new MyStruct(value);
        }
    }

머리가 아프다 ... 왜 안돼?
jasonh

2
흠, 나는 몇 달 전에 이것을 썼지 만 왜 정확히 일어 났는지 기억할 수 없습니다.
cbp 08-06-06

10
컴파일러 버그처럼 보입니다. += 500전화 : ldc.i4 500(500 Int32 형식 푸시), 후 call valuetype Program/MyStruct Program/MyStruct::op_Addition(valuetype Program/MyStruct, valuetype [mscorlib]System.Decimal)- 치료 A와 다음 그렇게 decimal변환하지 않고 (96 비트). 당신 += 500M이 그것을 사용 하면 그것을 올바르게 얻을 수 있습니다. 컴파일러 는 한 가지 방법으로 (아마도 암시 적 int 연산자로 인해) 할 수 있다고 생각 하고 다른 방법으로 결정하기로합니다.
Marc Gravell

1
이중 게시물에 대해 죄송합니다. 더 자세한 설명이 있습니다. 나는 이것을 추가 할 것이고, 나는 이것에 조금 익숙해졌으며 왜 그런 일이 일어나는 지 이해하지만 짜증이납니다. 나에게 이것은 불행히도 구조체 / 값 유형의 제한입니다. bytes.com/topic/net/answers/…
Bennett Dill

2
@ 컴파일러 오류가 발생하거나 원래 구조체에 영향을 미치지 않는 수정이 좋습니다. 액세스 위반은 상당히 다른 짐승입니다. 안전한 순수 관리 코드를 작성하는 경우 런타임은 절대로 그것을 던져서는 안됩니다.
코드 InChaos

18

C #은 배열이 다차원 적이 지 않고 형식간에 상속 관계가 있고 형식이 참조 형식 인 경우 배열과 목록 간의 변환을 지원합니다.

object[] oArray = new string[] { "one", "two", "three" };
string[] sArray = (string[])oArray;

// Also works for IList (and IEnumerable, ICollection)
IList<string> sList = (IList<string>)oArray;
IList<object> oList = new string[] { "one", "two", "three" };

이것은 작동하지 않습니다.

object[] oArray2 = new int[] { 1, 2, 3 }; // Error: Cannot implicitly convert type 'int[]' to 'object[]'
int[] iArray = (int[])oArray2;            // Error: Cannot convert type 'object[]' to 'int[]'

11
string []은 이미 ICloneable, IList, ICollection, IEnumerable, IList <string>, ICollection <string> 및 IEnumerable <string>을 구현하므로 IList <T> 예제는 캐스트입니다.
Lucas

15

이것은 내가 우연히 만난 가장 이상한 것입니다.

public class DummyObject
{
    public override string ToString()
    {
        return null;
    }
}

다음과 같이 사용됩니다 :

DummyObject obj = new DummyObject();
Console.WriteLine("The text: " + obj.GetType() + " is " + obj);

던질 것 NullReferenceException입니다. C # 컴파일러가 여러 개의 추가를 호출하여에 대한 컴파일을 수행합니다 String.Concat(object[]). .NET 4 이전에는 객체가 null인지 검사하지만 ToString ()의 결과가 아닌 Concat의 과부하에 버그가 있습니다.

object obj2 = args[i];
string text = (obj2 != null) ? obj2.ToString() : string.Empty;
// if obj2 is non-null, but obj2.ToString() returns null, then text==null
int length = text.Length;

이것은 ECMA-334 §14.7.4의 버그입니다.

이항 + 연산자는 피연산자 중 하나 또는 모두가 유형일 때 문자열 연결을 수행합니다 string. 문자열 연결 피연산자가 null인 경우 빈 문자열이 대체됩니다. 그렇지 않으면, 문자열이 아닌 피연산자는 ToStringtype에서 상속 된 가상 메소드 를 호출하여 문자열 표현으로 변환됩니다 object. ToString반환 null하면 빈 문자열이 대체됩니다.


3
흠,하지만이 결함이 .ToString실제로 null을 반환하지 않아야하지만 string.Empty를 반환해야 한다고 상상할 수 있습니다 . 그럼에도 불구하고 프레임 워크의 오류.
Dykam

12

흥미롭게도-처음 살펴봤을 때 C # 컴파일러가 확인하고 있다고 생각했지만 간섭 가능성을 제거하기 위해 직접 IL을 방출하더라도 여전히 발생합니다. 즉, 실제로 작동하는 newobjop 코드입니다 확인 중.

var method = new DynamicMethod("Test", null, null);
var il = method.GetILGenerator();

il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));

il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Newarr, typeof(char));
il.Emit(OpCodes.Newobj, typeof(string).GetConstructor(new[] { typeof(char[]) }));

il.Emit(OpCodes.Call, typeof(object).GetMethod("ReferenceEquals"));
il.Emit(OpCodes.Box, typeof(bool));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }));

il.Emit(OpCodes.Ret);

method.Invoke(null, null);

또한 이 op 코드가 빈 문자열을 인턴하기 위해 특별한 동작을 가져야한다는 것을 true확인 하는 경우 와 같습니다 string.Empty.


똑똑한 알렉이나 다른 것이 아니라 리플렉터에 대해 들어 보셨습니까 ? 이런 종류의 경우에는 매우 편리합니다.
RCIX

3
당신은 똑똑하지 않다. 당신은 요점을 잃어 버렸습니다-이 경우에 대한 특정 IL을 생성하고 싶었습니다. 어쨌든 Reflection.Emit이 이러한 유형의 시나리오에서 사소한 것이라면 C #에서 프로그램을 작성한 다음 리플렉터를 열고 바이너리를 찾고 메소드를 찾는 등의 속도가 빠를 것입니다. IDE를 그대로 두십시오.
Greg Beech

10
Public Class Item
   Public ID As Guid
   Public Text As String

   Public Sub New(ByVal id As Guid, ByVal name As String)
      Me.ID = id
      Me.Text = name
   End Sub
End Class

Public Sub Load(sender As Object, e As EventArgs) Handles Me.Load
   Dim box As New ComboBox
   Me.Controls.Add(box)          'Sorry I forgot this line the first time.'
   Dim h As IntPtr = box.Handle  'Im not sure you need this but you might.'
   Try
      box.Items.Add(New Item(Guid.Empty, Nothing))
   Catch ex As Exception
      MsgBox(ex.ToString())
   End Try
End Sub

"보호 된 메모리를 읽으려고했습니다. 다른 메모리가 손상되었음을 나타냅니다."


1
흥미 롭습니다! 그러나 컴파일러 버그처럼 들립니다. C #으로 포팅했으며 정상적으로 작동합니다. 즉, Load에 예외가 발생하면 많은 문제가 발생하며 디버거와 함께 / 디버거와 다르게 동작합니다. 디버거를 사용하여 잡을 수는 있지만 (어떤 경우에는) 없을 수도 있습니다.
Marc Gravell

죄송합니다. 양식을 추가하기 전에 콤보 상자를 양식에 추가해야합니다.
Joshua

이것은 일종의 끔찍한 내부 통신 메커니즘으로 SEH를 사용하는 대화 상자 초기화와 관련이 있습니까? Win32와 같은 것을 막연하게 기억합니다.
Daniel Earwicker 2012 년

1
이것은 위의 cbp와 동일한 문제입니다. 반환되는 값 유형은 사본이므로 해당 사본에서 파생 된 모든 속성에 대한 참조는 비트 버킷 토지로 향합니다. bytes.com/topic/net/answers/…
Bennett Dill

1
아니. 여기에는 구조체가 없습니다. 실제로 디버깅했습니다. 네이티브 콤보 상자의 목록 항목 컬렉션에 NULL이 추가되어 충돌이 지연됩니다.
Joshua

10

PropertyInfo.SetValue ()는 정수를 열거 형에, 정수를 nullable 정수에, 열거 형을 nullable 열거 형에 할당 할 수 있지만 int는 nullable 열거 형에 할당 할 수 없습니다.

enumProperty.SetValue(obj, 1, null); //works
nullableIntProperty.SetValue(obj, 1, null); //works
nullableEnumProperty.SetValue(obj, MyEnum.Foo, null); //works
nullableEnumProperty.SetValue(obj, 1, null); // throws an exception !!!

전체 설명은 여기


10

형식 인수에 따라 모호하게 만들 수있는 메서드가있는 일반 클래스가 있다면 어떨까요? 나는 최근 양방향 사전을 작성 하여이 상황에 부딪쳤다. Get()전달 된 인수의 반대를 반환하는 대칭 메서드 를 작성하고 싶었습니다 . 이 같은:

class TwoWayRelationship<T1, T2>
{
    public T2 Get(T1 key) { /* ... */ }
    public T1 Get(T2 key) { /* ... */ }
}

당신이 인스턴스를 만들 경우 경우 모든 잘 좋은 T1T2다른 유형이 있습니다 :

var r1 = new TwoWayRelationship<int, string>();
r1.Get(1);
r1.Get("a");

그러나 T1and T2가 같으면 (아마도 하나가 다른 것의 서브 클래스 인 경우) 컴파일러 오류입니다.

var r2 = new TwoWayRelationship<int, int>();
r2.Get(1);  // "The call is ambiguous..."

흥미롭게도, 두 번째 경우의 다른 모든 방법은 여전히 ​​사용 가능합니다. 컴파일러 오류를 일으키는 모호한 메소드 만 호출합니다. 흥미롭지 않은 경우가 조금 희미하고 모호합니다.


메소드 오버로드의 반대자들은 이것을 좋아할 것입니다 ^^.
Christian Klauser

1
잘 모르겠습니다. 이것은 제게 의미가 있습니다.
Scott Whitlock

10

C # 접근성 퍼즐


다음 파생 클래스는 기본 클래스에서 개인 필드에 액세스 하고 컴파일러는 다른 쪽을 자동으로 찾습니다.

public class Derived : Base
{
    public int BrokenAccess()
    {
        return base.m_basePrivateField;
    }
}

이 필드는 실제로 비공개입니다.

private int m_basePrivateField = 0;

그러한 코드를 어떻게 컴파일 할 수 있을까?

.

.

.

.

.

.

.

대답


속임수는 Derived내부 클래스 로 선언 하는 것입니다 Base.

public class Base
{
    private int m_basePrivateField = 0;

    public class Derived : Base
    {
        public int BrokenAccess()
        {
            return base.m_basePrivateField;
        }
    }
}

내부 클래스는 외부 클래스 멤버에게 완전히 액세스 할 수 있습니다. 이 경우 내부 클래스는 외부 클래스에서도 파생됩니다. 이를 통해 개인 구성원의 캡슐화를 "중단"할 수 있습니다.


그것은 실제로 잘 문서화되어 있습니다. msdn.microsoft.com/en-us/library/ms173120%28VS.80%29.aspx . 특히 외부 클래스가 정적 인 경우 유용한 기능이 될 수 있습니다.

예-물론 문서화되어 있습니다. 그러나이 퍼즐을 해결 한 사람은 거의 없으므로 멋진 퀴즈라고 생각했습니다.
Omer Mor

2
내부 클래스가 소유자를 상속함으로써 스택 오버플로 가능성이 매우 높은 것 같습니다.
Jamie Treworgy

또 다른 유사하고 완벽하게 올바른 경우는 객체가 동일한 유형의 다른 객체의 개인 멤버에 액세스 할 수 있다는 것입니다.class A { private int _i; public void foo(A other) { int res = other._i; } }
Olivier Jacot-

10

오늘 좋은 작은 것을 발견했습니다.

public class Base
{
   public virtual void Initialize(dynamic stuff) { 
   //...
   }
}
public class Derived:Base
{
   public override void Initialize(dynamic stuff) {
   base.Initialize(stuff);
   //...
   }
}

컴파일 오류가 발생합니다.

'Initialize'메소드에 대한 호출은 동적으로 디스패치되어야하지만 기본 액세스 표현식의 일부이기 때문에 불가능합니다. 동적 인수를 캐스트하거나 기본 액세스를 제거하십시오.

base.Initialize (객체로 물건)를 쓰면; 그것은 완벽하게 작동하지만, 이것은 정확히 "똑같은 일이기 때문에 모든 것이 여전히 역동적 인 것으로 받아 들여지기 때문에 여기서"마법의 단어 "인 것 같습니다 ...


8

우리가 사용하는 API에서 도메인 객체를 반환하는 메소드는 특별한 "널 객체"를 반환 할 수 있습니다. 이를 구현할 때 비교 연산자와 Equals()메소드가 와 비교 true되면 리턴되도록 대체됩니다 null.

따라서이 API 사용자에게는 다음과 같은 코드가있을 수 있습니다.

return test != null ? test : GetDefault();

또는 아마도 다음과 같이 조금 더 장황합니다.

if (test == null)
    return GetDefault();
return test;

where GetDefault()대신에 사용하려는 일부 기본값을 반환하는 메소드가 있습니다 null. ReSharper를 사용할 때 다음 중 하나를 다시 작성하는 것이 좋습니다.

return test ?? GetDefault();

테스트 객체가 적절한 API 대신 API에서 반환 된 null 객체 인 null경우 null 통합 연산자가 실제로 또는 null실행 중이 아닌을 검사하므로 코드 동작이 변경되었습니다 .operator=Equals()


1
실제로 AC # 코너 케이스가 아니라 그것을 생각한 사랑하는 주 님?!?
Ray Booysen

이 코드는 nullable 형식 만 사용하고 있지 않습니까? 따라서 ReSharper는 "??" 사용하다. Ray가 말했듯이, 나는 이것이 코너 사건이라고 생각하지 않았을 것입니다. 아니면 내가 틀렸어?
Tony

1
예, 유형은 nullable이며 NullObject도 있습니다. 모퉁이의 경우는 모르겠지만 적어도 'if (a! = null)이 a를 반환하는 경우입니다. 리턴 b; ' 'return a ??'와 동일하지 않습니다. 비'. 나는 그것이 프레임 워크 / API 디자인에 문제가 있음을 절대적으로 동의합니다-오버로드 == null 객체에서 true를 반환하는 것은 좋은 생각이 아닙니다!
Tor Livar

8

이 이상한 경우를 고려하십시오.

public interface MyInterface {
  void Method();
}
public class Base {
  public void Method() { }
}
public class Derived : Base, MyInterface { }

경우 BaseDerived동일한 어셈블리에 선언, 컴파일러는 것 Base::Method, 비록합니다 (CIL)에서 가상 및 밀봉Base 인터페이스를 구현하지 않습니다.

경우 BaseDerived다른 어셈블리에있는을 컴파일 할 때, Derived조립이에 멤버를 소개합니다, 그래서 컴파일러는, 다른 어셈블리를 변경하지 않습니다 Derived에 대한 명시 적으로 구현 될 것 MyInterface::Method그건 그냥 호출을 위임합니다 Base::Method.

컴파일러는 인터페이스와 관련하여 다형성 디스패치를 ​​지원하기 위해이 작업을 수행해야합니다. 즉, 해당 메서드를 가상으로 만들어야합니다.


정말 이상하게 들립니다. 나중에 조사해야 할 것입니다 :)
Jon Skeet

@ Jon Skeet : C #의 역할에 대한 구현 전략 을 조사하면서 이것을 발견했습니다 . 그에 대한 의견을 보내 주시면 감사하겠습니다.
Jordão

7

다음은 제가 단순히 부족한 일반적인 지식 일 수 있습니다. 얼마 전에는 가상 속성이 포함 된 버그 사례가있었습니다. 컨텍스트를 약간 추상화하고 다음 코드를 고려하여 지정된 영역에 중단 점을 적용하십시오.

class Program
{
    static void Main(string[] args)
    {
        Derived d = new Derived();
        d.Property = "AWESOME";
    }
}

class Base
{
    string _baseProp;
    public virtual string Property 
    { 
        get 
        {
            return "BASE_" + _baseProp;
        }
        set
        {
            _baseProp = value;
            //do work with the base property which might 
            //not be exposed to derived types
            //here
            Console.Out.WriteLine("_baseProp is BASE_" + value.ToString());
        }
    }
}

class Derived : Base
{
    string _prop;
    public override string Property 
    {
        get { return _prop; }
        set 
        { 
            _prop = value; 
            base.Property = value;
        } //<- put a breakpoint here then mouse over BaseProperty, 
          //   and then mouse over the base.Property call inside it.
    }

    public string BaseProperty { get { return base.Property; } private set { } }
}

에있는 동안 Derived추가 할 때 개체 컨텍스트, 같은 동작을 얻을 수있는 base.Property시계, 또는 입력으로 base.Property간략한에.

무슨 일이 있었는지 깨달았어요 결국 나는 퀵 워치에 의해 깨달았습니다. Quickwatch로 이동하여 Derived객체 d (또는 객체의 컨텍스트에서 this)를 탐색하고 필드를 선택 base하면 Quickwatch 상단의 편집 필드에 다음 캐스트가 표시됩니다.

((TestProject1.Base)(d))

즉,베이스가 이와 같이 교체되면 통화는

public string BaseProperty { get { return ((TestProject1.Base)(d)).Property; } private set { } }

Watches, Quickwatch 및 디버깅 마우스 오버 툴팁에 대해 다형성을 고려할 때 "AWESOME"대신 표시하는 것이 좋습니다 "BASE_AWESOME". 나는 그것이 왜 그것을 캐스트로 변환 할 것인지 확실하지 call않습니다. 하나의 가설은 해당 모듈의 컨텍스트에서 사용 가능하지 않을 수 있다는 것 callvirt입니다.

어쨌든, 기능면에서 아무것도 변경하지 않는 것은 Derived.BaseProperty여전히 실제로 반환 될 것입니다. "BASE_AWESOME"따라서 이것은 직장에서의 버그의 근본이 아니며 단순히 혼란스러운 구성 요소입니다. 그러나 디버그 세션 중에 해당 사실을 알지 못하는 개발자를 어떻게 잘못 인도 할 수 있는지 흥미 롭습니다. 특히 Base프로젝트에 노출되지 않고 타사 DLL로 참조되어 Dev가 다음과 같이 말한 경우 :

"Oi, wait..what? omg that DLL is like ..doing something funny"


그것은 특별한 것이 아니며 작업을 재정의하는 방법입니다.
구성자

7

이건 꽤 어렵습니다. Begin / EndInvoke를 실제로 지원하는 RealProxy 구현을 구축하려고 시도하는 동안 문제가 발생했습니다 (MS는 끔찍한 해킹없이 수행 할 수 없기 때문에 감사합니다). 이 예제는 기본적으로 CLR의 버그이며 BeginInvoke의 관리되지 않는 코드 경로는 RealProxy.PrivateInvoke (및 내 호출 재정의)의 반환 메시지가 IAsyncResult의 인스턴스를 반환하는지 확인하지 않습니다. 일단 반환되면 CLR은 매우 혼란스러워지고 하단의 테스트에서 알 수 있듯이 무슨 일이 일어나고 있는지 전혀 알지 못합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Reflection;
using System.Runtime.Remoting.Messaging;

namespace BrokenProxy
{
    class NotAnIAsyncResult
    {
        public string SomeProperty { get; set; }
    }

    class BrokenProxy : RealProxy
    {
        private void HackFlags()
        {
            var flagsField = typeof(RealProxy).GetField("_flags", BindingFlags.NonPublic | BindingFlags.Instance);
            int val = (int)flagsField.GetValue(this);
            val |= 1; // 1 = RemotingProxy, check out System.Runtime.Remoting.Proxies.RealProxyFlags
            flagsField.SetValue(this, val);
        }

        public BrokenProxy(Type t)
            : base(t)
        {
            HackFlags();
        }

        public override IMessage Invoke(IMessage msg)
        {
            var naiar = new NotAnIAsyncResult();
            naiar.SomeProperty = "o noes";
            return new ReturnMessage(naiar, null, 0, null, (IMethodCallMessage)msg);
        }
    }

    interface IRandomInterface
    {
        int DoSomething();
    }

    class Program
    {
        static void Main(string[] args)
        {
            BrokenProxy bp = new BrokenProxy(typeof(IRandomInterface));
            var instance = (IRandomInterface)bp.GetTransparentProxy();
            Func<int> doSomethingDelegate = instance.DoSomething;
            IAsyncResult notAnIAsyncResult = doSomethingDelegate.BeginInvoke(null, null);

            var interfaces = notAnIAsyncResult.GetType().GetInterfaces();
            Console.WriteLine(!interfaces.Any() ? "No interfaces on notAnIAsyncResult" : "Interfaces");
            Console.WriteLine(notAnIAsyncResult is IAsyncResult); // Should be false, is it?!
            Console.WriteLine(((NotAnIAsyncResult)notAnIAsyncResult).SomeProperty);
            Console.WriteLine(((IAsyncResult)notAnIAsyncResult).IsCompleted); // No way this works.
        }
    }
}

산출:

No interfaces on notAnIAsyncResult
True
o noes

Unhandled Exception: System.EntryPointNotFoundException: Entry point was not found.
   at System.IAsyncResult.get_IsCompleted()
   at BrokenProxy.Program.Main(String[] args) 

6

이것이 Windows Vista / 7 이상한지 또는 .Net 이상한지 확실하지 않지만 잠시 동안 내 머리를 긁적였습니다.

string filename = @"c:\program files\my folder\test.txt";
System.IO.File.WriteAllText(filename, "Hello world.");
bool exists = System.IO.File.Exists(filename); // returns true;
string text = System.IO.File.ReadAllText(filename); // Returns "Hello world."

Windows Vista / 7에서 파일은 실제로 C:\Users\<username>\Virtual Store\Program Files\my folder\test.txt


2
이것은 실제로 비스타 (아 파크 아님) 보안 강화입니다. 그러나 멋진 점은 프로그램 파일 경로로 파일을 읽고 열 수 있다는 것입니다. 탐색기를 사용하면 파일이 없습니다. 내가 마침내 그것을 발견하기 전에 이것은 하나의 고객 @ @ 하루 거의 일했다.
Henri

확실히 Windows 7이기도합니다. 그것이 내가 그것을 만났을 때 내가 사용했던 것입니다. 나는 그 이유를 이해하지만 여전히 알아 차리기가 어려웠다.
스펜서 루 포트

Vista / Win 7 (기술적으로도 WinXP)에서 응용 프로그램은 기술적으로 사용자 데이터로 사용자 폴더 토지의 AppData 폴더에 기록해야합니다. 응용 프로그램은 관리자 권한이없는 한 프로그램 파일 / windows / system32 / etc에 기록해서는 안되며 해당 권한은 프로그램 업그레이드 / 제거 / 새로운 기능 설치를 말해야합니다. 그러나! 여전히 system32 / windows / etc에 쓰지 마십시오 :) 위 코드를 관리자로 실행 한 경우 (마우스 오른쪽 버튼 클릭> 관리자 권한으로 실행) 이론적으로 프로그램 파일 앱 폴더에 쓰십시오.
Steve Syfuhs


6

C # 컴파일러가 잘못된 CIL을 생성 할 수 있다고 생각한 적이 있습니까? 이것을 실행하면 TypeLoadException:

interface I<T> {
  T M(T p);
}
abstract class A<T> : I<T> {
  public abstract T M(T p);
}
abstract class B<T> : A<T>, I<int> {
  public override T M(T p) { return p; }
  public int M(int p) { return p * 2; }
}
class C : B<int> { }

class Program {
  static void Main(string[] args) {
    Console.WriteLine(new C().M(42));
  }
}

그래도 C # 4.0 컴파일러에서 어떻게 작동하는지 모르겠습니다.

편집 : 이것은 내 시스템의 출력입니다.

C:\Temp>type Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

  interface I<T> {
    T M(T p);
  }
  abstract class A<T> : I<T> {
    public abstract T M(T p);
  }
  abstract class B<T> : A<T>, I<int> {
    public override T M(T p) { return p; }
    public int M(int p) { return p * 2; }
  }
  class C : B<int> { }

  class Program {
    static void Main(string[] args) {
      Console.WriteLine(new C().M(11));
    }
  }

}
C:\Temp>csc Program.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Temp>Program

Unhandled Exception: System.TypeLoadException: Could not load type 'ConsoleAppli
cation1.C' from assembly 'Program, Version=0.0.0.0, Culture=neutral, PublicKeyTo
ken=null'.
   at ConsoleApplication1.Program.Main(String[] args)

C:\Temp>peverify Program.exe

Microsoft (R) .NET Framework PE Verifier.  Version  3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

[token  0x02000005] Type load failed.
[IL]: Error: [C:\Temp\Program.exe : ConsoleApplication1.Program::Main][offset 0x
00000001] Unable to resolve token.
2 Error(s) Verifying Program.exe

C:\Temp>ver

Microsoft Windows XP [Version 5.1.2600]

C # 3.5 컴파일러와 C # 4 컴파일러에서 모두 작동합니다.
Jon Skeet

내 시스템에서는 작동하지 않습니다. 질문에 결과를 붙여 넣을 것입니다.
Jordão

.NET 3.5에서는 실패했습니다 (4.0을 테스트 할 시간이 없습니다). VB.NET 코드로 문제를 복제 할 수 있습니다.
Mark Hurd

3

클로저를 처리하는 방식 인 C #에는 정말 흥미로운 점이 있습니다.

스택 변수 값을 클로저 프리 변수에 복사하는 대신, 전 처리기 매직은 변수의 모든 발생을 객체로 래핑하여 스택에서 힙으로 바로 이동시킵니다! :)

ML # (AFAIK를 사용하는 스택 값 복사를 사용하는)보다 C #이 기능적으로 완전한 (또는 람다 완료 된) 언어를 훨씬 더 많이 만듭니다. C #과 마찬가지로 F #에도 해당 기능이 있습니다.

그것은 저에게 많은 즐거움을 가져다줍니다. MS 여러분 감사합니다!

이상한 점이나 코너 케이스는 아니지만 스택 기반 VM 언어에서 실제로 예상치 못한 것입니다. :)


3

얼마 전에 내가 물었던 질문에서 :

조건부 연산자는 암시 적으로 캐스트 할 수 없습니까?

주어진:

Bool aBoolValue;

어디에 aBoolValueTrue 또는 False가 할당됩니다.

다음은 컴파일되지 않습니다 :

Byte aByteValue = aBoolValue ? 1 : 0;

그러나 이것은 :

Int anIntValue = aBoolValue ? 1 : 0;

제공된 답변도 꽤 좋습니다.


비록 ve not test it I이것이 작동 할 것이라고 확신 하지만 : Byte aByteValue = aBoolValue? (바이트) 1 :( 바이트) 0; 또는 : 바이트 aByteValue = (바이트) (aBoolValue? 1 : 0);
Alex Pacurar

2
그렇습니다, 알렉스. 열쇠는 암시 적 캐스팅에 있습니다. 1 : 0혼자서는 바이트가 아닌 int로 캐스트됩니다.
MPelletier

2

C #의 범위는 때때로 기괴합니다. 한 가지 예를 들어 보겠습니다.

if (true)
{
   OleDbCommand command = SQLServer.CreateCommand();
}

OleDbCommand command = SQLServer.CreateCommand();

명령이 다시 선언 되었기 때문에 컴파일에 실패합니까? 왜이 스레드에서 스택 오버플로내 블로그 에서 그렇게 작동하는지에 대한 흥미로운 추측이 있습니다.


34
나는 이것을 기괴한 것으로 보지 않습니다. 블로그에서 "완벽하게 올바른 코드"라고 부르는 것은 언어 사양에 따라 완전히 틀립니다 . 그것은 당신이 거라고 어떤 상상의 언어로 올바른있을 수 있습니다 같은 수의 C #,하지만 언어 사양은 C #으로는 유효하다는 매우 분명하다.
Jon Skeet

7
C / C ++에서 유효합니다. 그리고 그것이 C #이기 때문에 여전히 작동하기를 원했을 것입니다. 가장 큰 버그는 컴파일러가 이것을 할 이유가 없다는 것입니다. 중첩 범위 지정을 수행하는 것이 어렵지 않습니다. 나는 모든 것이 가장 놀람의 요소로 귀결된다고 생각합니다. 스펙이 이것과 저것을 말할 수는 있지만 그것이 그렇게 행동하는 것이 완전히 비논리적이라면 실제로 도움이되지 않습니다.
Anders Rune Jensen

6
C #! = C / C ++. cout << "Hello World!"도 사용 하시겠습니까? << endl; Console.WriteLine ( "Hello World!"); 대신에? 또한 비논리적 인 것이 아니라 사양을 읽으십시오.
Kredns

9
언어의 핵심 부분 인 범위 지정 규칙에 대해 이야기하고 있습니다. 표준 라이브러리에 대해 말하고 있습니다. 그러나 이제 프로그래밍을 시작하기 전에 C # 언어의 작은 사양을 읽어야한다는 것이 분명합니다.
Anders Rune Jensen

6
에릭 Lippert의 사실 C #은 최근이 같은 설계 이유를 게시 : blogs.msdn.com/ericlippert/archive/2009/11/02/...를 . 요약은 변경 사항이 의도하지 않은 결과를 초래할 가능성이 적기 때문입니다.
Helephant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.