Programing

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

lottogame 2020. 3. 6. 08:17
반응형

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


나는 몇 가지 코너 케이스와 뇌 맛보기를 수집하고 항상 더 많은 것을 듣고 싶습니다. 이 페이지는 실제로 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를 인쇄하고 테스트 한 모든 버전의 프레임 워크에서 수행했습니다. (필자는 분명히 Mono에서 시도하지 않았습니다 ...)

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

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


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

    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. 자 가서 눈을 씻으십시오!


은행가의 반올림.

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

.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에서도 마찬가지입니다.


이 함수는 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 비트 머신이 없지만 메소드는 테일 콜 최적화를위한 모든 조건을 충족합니다. 아무도 가지고 있지 않다면 나는 그것이 사실인지 알고 싶습니다.


이것을 할당하십시오!


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

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

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

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

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

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

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

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


몇 년 전, 충성도 프로그램을 진행할 때 고객에게 제공되는 포인트의 양에 문제가있었습니다. 이 문제는 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);

열거 형 함수 과부하가 있어도 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");
        }
    }
}

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

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

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

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


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

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

위의 내용은 언뜻보기에는 미쳤지 만 실제로는 합법적입니다 . 아니요 , 실제로 (핵심 부분을 놓쳤지만 "클래스 추가 "또는 " 별칭을 추가하여 별명 추가" 와 같은 해킹 은 아닙니다 . 수업").IFoousingIFoo

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


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

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

해킹 된 부울


나는 파티에 조금 늦게 도착하고 있지만 3 4 5가 있습니다.

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


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

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

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

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

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


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

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

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

stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);

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

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

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

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


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

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

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

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

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[]'

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

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하면 빈 문자열이 대체됩니다.


흥미롭게도-처음 살펴봤을 때 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.


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

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


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

전체 설명은 여기


형식 인수에 따라 모호하게 만들 수있는 메서드가있는 일반 클래스가 있다면 어떻습니까? 나는 최근 양방향 사전을 작성 하여이 상황에 부딪쳤다. 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..."

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


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

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


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

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


우리가 사용하는 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에서 반환 된 null 객체 인 null경우 null 통합 연산자가 실제로 또는 null실행 중이 아닌을 검사하므로 코드 동작이 변경되었습니다 .operator=Equals()


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

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.

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


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

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 like, ..doing something funny"


이건 꽤 어렵습니다. 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) 

이것이 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


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 #에는 정말 흥미로운 점이 있습니다.

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

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

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

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


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

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

주어진:

Bool aBoolValue;

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

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

Byte aByteValue = aBoolValue ? 1 : 0;

그러나 이것은 :

Int anIntValue = aBoolValue ? 1 : 0;

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


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

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

OleDbCommand command = SQLServer.CreateCommand();

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

참고 URL : https://stackoverflow.com/questions/194484/whats-the-strangest-corner-case-youve-seen-in-c-sharp-or-net



반응형