it-source

C# 또는 에서 본 것 중 가장 이상한 코너 케이스는 무엇입니까?NET?

criticalcode 2023. 5. 16. 22:44
반응형

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로 인쇄되며, 제가 테스트한 프레임워크의 모든 버전에서 작업을 수행했습니다. (솔직히 모노에서 사용해 본 적이 없습니다.)

분명히 말씀드리자면, 이것은 제가 찾고 있는 종류의 예일 뿐입니다. 저는 특별히 이 이상함에 대한 토론/설명을 찾고 있었던 것은 아닙니다.(이것은 일반적인 문자열 인터닝과는 다릅니다. 특히, 생성자가 호출될 때 문자열 인터닝은 일반적으로 발생하지 않습니다.)나는 정말로 비슷한 이상한 행동을 요구하고 있었습니다.

다른 보석들이 숨어 있나요?

전에 보여드린 것 같은데, 여기 재미있는 점이 마음에 듭니다. 추적하는 데 디버깅이 좀 걸렸습니다! (원래 코드는 분명히 더 복잡하고 미묘했습니다...)

    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)로 캐스팅됩니다. -pnull -p는 GetType()...을합니다.


업데이트: 플롯이 두꺼워집니다...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 { }

이이제에있으면자리것,면▁the,new()("출이프리됩니다션렉호디로록시▁callMyFunnyProxyAttribute), 를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...

이로 인해 더 잘 알려진 라운드-하프-업 반올림을 기반으로 한 재무 계산에서 예상치 못한 버그가 발생할 수 있습니다.

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

이 함수는 다음과 같이 호출될 경우 무엇을 합니까?Rec(0)(디버거 아래가 아님)?

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

답변:

  • 32비트 JIT에서는 StackOverflow가 발생합니다.예외.
  • 64비트 JIT에서는 int에 모든 숫자를 인쇄해야 합니다.최대값

는 64비트 J가IT 컴파일러는 테일최적화를 적용하지만 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 d = 13.6;

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

i1 == i2 인가요?

알고 보니 i1!= i2였습니다.변환 연산자와 캐스트 연산자의 반올림 정책이 다르기 때문에 실제 값은 다음과 같습니다.

i1 == 14
i2 == 13

항상 수학을 부르는 것이 좋습니다.천장() 또는 수학.바닥()(또는 수학).요구 사항을 충족하는 중간점 라운드로 라운드)

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

그것은 당신이 그것을 선언할 수 있지만, 그것은 당신이 중앙에 있는 어떤 클래스든 다른 거북이와 함께 포장하도록 항상 요청할 것이기 때문에 실질적인 용도가 없습니다.

[농담] 거북이가 계속 아래쪽에 있는 것 같아요.[/two]

여기 최근에 알게 된 것이 있습니다.

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

위의 내용은 언뜻 보기에는 미친 것처럼 보이지만 실제로는 합법적입니다.아니요, 정말 (비록 제가 중요한 부분을 놓쳤지만, 그것은 "라고 불리는 수업을 추가하는 것과 같은 진부한 것은 아닙니다.IFoo" 는또 "가추"를 추가합니다.using 에대 별칭한IFoo수업에서").

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

부울이 참도 거짓도 아닌 경우는 언제입니까?

Bill은 A가 True이고 B가 True이면 (A와 B)가 False가 되도록 부울을 해킹할 수 있다는 것을 발견했습니다.

해킹당한 불란

파티에 조금 늦게 도착할 예정입니다만, 저는 시간이 있습니다. 세 개 5:

  1. 로드/표시되지 않은 컨트롤에서 InvokeRequired를 폴링하면 false -로 표시되고 다른 스레드에서 변경하려고 하면 얼굴이 터집니다(해결책은 이를 참조하는 것입니다).컨트롤 작성자에서 핸들)을 누릅니다.

  2. 저를 넘어뜨린 또 다른 것은 다음과 같은 어셈블리가 주어졌다는 것입니다.

    enum MyEnum
    {
        Red,
        Blue,
    }
    

    MyEnum을 계산하면.다른 어셈블리의 Red.ToString() 및 그 사이에 누군가가 사용자의 열거를 다시 컴파일했습니다.

    enum MyEnum
    {
        Black,
        Red,
        Blue,
    }
    

    런타임에 "Black"이 표시됩니다.

  3. 저는 유용한 상수가 있는 공유 어셈블리를 가지고 있었습니다.제 전임자가 보기 흉하게 생긴 부동산을 많이 남겼기 때문에, 저는 잡동사니를 없애고 공공 컨스트를 사용해야겠다고 생각했습니다.VS가 참조가 아닌 가치에 따라 데이터를 편집했을 때 저는 매우 놀랐습니다.

  4. 다른 어셈블리에서 인터페이스의 새 메서드를 구현했지만 해당 어셈블리의 이전 버전을 참조하여 다시 빌드하면 TypeLoadException('NewMethod' 구현 없음)이 표시됩니다(여기 참조).

  5. 사전 <,>: "물건이 반환되는 순서가 정의되지 않았습니다."이것은 끔찍합니다. 왜냐하면 그것은 때때로 여러분을 물 수 있지만 다른 사람들은 일을 할 수 있기 때문입니다. 그리고 만약 여러분이 사전이 잘 작동할 것이라고 맹목적으로 가정했다면 ("왜 그렇게 하면 안 될까요?")저는, "목록은 그렇다"고 생각했습니다. 당신이 마침내 당신의 가정에 의문을 제기하기 전에, 당신은 정말로 그것에 참견해야 합니다.

VB.NET, null 및 3진수 연산자:

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

데 이 좀 디버그를 입니다. 제가 예상했던 것보다i다담을 Nothing.

내가 정말로 무엇을 포함하고 있을까요? 0.

이는 놀랍지만 실제로는 "올바른" 행동입니다.Nothing이 VB와 .NET과(와) 정확히 동일하지 않습니다.null CLR:Nothing을 의미할 수도 있습니다.null또는default(T) 유형 값 형 의 경 우유우에 T문맥위의따 경우라에,IfInteger 유인으로의 Nothing그리고.5이 그서래, 이경는에우,Nothing은 단입니다.0.

저는 제 첫 번째 케이스를 훨씬 능가하는 두 번째 정말 이상한 코너 케이스를 발견했습니다.

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

저는 어떤 기능의 맨 위에 줄에 이것이 있는 코드 블록을 작업하고 있었습니다.

stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);

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

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

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

지금쯤이면 당신에게 말할 필요가 없을 겁니다.문자열 비교에서 이전에 사용되지 않은 문화를 사용하면 어셈블리 로드가 발생합니다.불변의 문화도 예외는 아닙니다.

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

다음 장치 테스트는 문제를 보여줍니다.

무엇이 잘못되었는지 알아낼 수 있는지 보세요.

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

을 던질 입니다.NullReferenceExceptionC# 컴파일러가 여러 추가 사항을 컴파일하여 호출하는 것으로 나타났습니다.String.Concat(object[]). 4,null인지 되지 않습니다.NET 4, 객체가 null인지 확인되는 Concat의 오버로드에만 버그가 있지만 ToString()의 결과는 아닙니다.

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빈 문자열이 대체되었습니다.그렇지 " 지렇않문이아피가는자상호문표다변니환됩자현로으열그여출"를 호출하여 문자열 표현으로 됩니다.ToString에서 object반환되는 경우 빈 문자열이 대체됩니다.

- 에 C#하고 있는 했을 때, IL을 합니다. 즉, 는 흥롭요 - 처음에 C# 컴가고때을있확을는하발여다니합히방생전더라도하직출위미해접제기간섭하거네가을성능가것고이라인정했러일파▁▁is▁it을▁the▁which▁means▁when,여발다흥#니합생히전방▁reallying미도라더하▁but▁directly▁was▁-▁still▁happens출ference▁the▁it▁even▁i#직▁looked▁i 이것은 정말로 그것이 발생한다는 것을 의미합니다.newobj검사를 하는 op-code.

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

또한 다음과 같습니다.true 당신이 면해보조에 대해 한다면.string.Empty즉, 이 op-code는 빈 문자열을 삽입하기 위해 특수한 동작을 수행해야 합니다.

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

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

속성 정보.SetValue()는 int를 열거형에, int를 null 가능한 int에, enum을 null 가능한 enum에 할당할 수 있지만 int를 null 가능한 enum에 할당할 수 없습니다.

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) { /* ... */ }
}

다 요. 예를 .T1그리고.T2다양한 유형:

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

하지만 만약에T1그리고.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'에 대한 호출은 동적으로 디스패치되어야 하지만 기본 액세스 식의 일부이기 때문일 수 없습니다.동적 인수를 캐스팅하거나 기본 액세스를 제거하는 것을 고려합니다.

베이스를 쓰면.초기화(객체로 사용); 완벽하게 작동하지만, 여기서는 "마법의 단어"로 보입니다. 정확히 동일하게 작동하기 때문에 모든 것이 여전히 동적으로 수신됩니다.

사용 중인 API에서 도메인 개체를 반환하는 메서드는 특수한 "null 개체"를 반환할 수 있습니다.할 때, 와 이를때구, 비연자와.Equals()하여 드가도재정었습니다되의록메반서하환 합니다.true와 하면.null.

따라서 이 API의 사용자는 다음과 같은 코드를 사용할 수 있습니다.

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

또는 다음과 같이 좀 더 장황하게 설명할 수 있습니다.

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

GetDefault()대신 사용할 기본값을 반환하는 방법입니다.null제가 ReSharper를 사용하고 있을 때, 이 중 하나를 다음으로 다시 쓰라는 권장 사항에 따라 깜짝 놀랐습니다.

return test ?? GetDefault();

가 아닌 인 nullNull 병합 연산자가 실제로 확인하는 것처럼 코드의 동작이 이제 변경되었습니다.null 중이 아닌operator=또는Equals().

이 이상한 경우를 생각해 보십시오.

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

한다면Base그리고.Derived동일한 어셈블리에 선언되면 컴파일러는Base::Method 및 에서), 가상및밀봉(CIL▁though도Base인터페이스를 구현하지 않습니다.

한다면Base그리고.Derived는 컴일할때어있습다니에리셈블른을 할 때 어셈블리에 있습니다.Derived어셈블리, 컴파일러는 다른 어셈블리를 변경하지 않으므로 구성원을 소개합니다.Derived은 그은명확구될것다니입현이한것ation▁에 대한 명시적인 구현이 될 것입니다.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 시로타으, 핑이로또는계로으▁as를 타이핑합니다.base.Property퀵워치로

무슨 일이 일어나고 있는지 깨닫는데 시간이 좀 걸렸습니다.결국 저는 Quickwatch에 의해 깨달음을 얻었습니다. Quickwatch를 을 참조하십시오.Derived d d)this및 )을 참조하십시오.baseQuickwatch 상단의 편집 필드에는 다음과 같은 캐스트가 표시됩니다.

((TestProject1.Base)(d))

그 말은 만약 기지가 교체된다면, 통화는

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

및 오버, Quickwatch를 하면 됩니다."AWESOME""BASE_AWESOME"다형성을 고려할 때.잘 요, 한 은 왜캐팅이됐잘모아, 요가설은가지한어겠르는직지도스▁i▁thatis▁is가은,설▁hypothes▁would▁cast▁into▁it▁it한가▁a▁one,지왜▁transform요▁unsure어'.call모듈의할 수 없을 수 , 사용할 수 있는 모듈은 "" ""뿐입니다.callvirt.

, 어든인면에는서분명않아바다습뀌니지무도것히쨌기능적▁anyhow.Derived.BaseProperty여전히 정말로 돌아올 것입니다."BASE_AWESOME"그래서 이것은 우리가 일하는 버그의 근원이 아니라 단지 혼란스러운 구성요소였습니다.세션 이 그 것을 오해할 수 있는지 로웠습니다. "Debug Session"이라면 말이죠.Base는 다음과 같이만

"오이, 잠깐..뭐라고요? 맙소사, DLL은 재미있는 것을 하는 것과 같습니다."

이건 위에 올리기가 꽤 어렵군요.Begin/EndInvoke를 진정으로 지원하는 RealProxy 구현을 구축하려고 시도하던 중 우연히 이 문제를 발견했습니다(끔찍한 해킹 없이는 불가능하게 만든 MS에 감사합니다).이 예는 기본적으로 Begin의 관리되지 않는 코드 경로인 CLR의 버그입니다.호출은 RealProxy의 반환 메시지를 확인하지 않습니다.PrivateInvoke(및 내 Invoke 재정의)가 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 이상한 버전인지 아니면 a라고 말할지 모르겠습니다.이상하긴 하지만 한동안 머리를 긁적거리기도 했습니다.

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

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

스택 변수 값을 폐쇄 없는 변수에 복사하는 대신 변수의 모든 발생 항목을 개체로 래핑하는 전처리 마법을 수행하여 스택 밖으로 이동합니다. 힙으로 바로 이동합니다! :)

제 생각에, 그것은 C#을 ML 자체(또는 AFAIK를 복사하는 스택 값을 사용하는)보다 훨씬 더 기능적으로 완전한(또는 람다-완전한) 언어로 만듭니다.F#에도 C#과 마찬가지로 이러한 기능이 있습니다.

그것은 저에게 큰 기쁨을 가져다 줍니다, MS 여러분 감사합니다!

이상한 사건이나 구석 사건은 아니지만...하지만 스택 기반 VM 언어에서는 예상치 못한 것이 있습니다. :)

얼마 전에 제가 한 질문에서:

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

주어진:

Bool aBoolValue;

에▁where디aBoolValueTrue 또는 False 중 하나가 할당됩니다.

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

Byte aByteValue = aBoolValue ? 1 : 0;

하지만 이는 다음과 같습니다.

Int anIntValue = aBoolValue ? 1 : 0;

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

c#의 범위 지정은 때때로 정말 이상합니다.한 가지 예를 들어 보겠습니다.

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

OleDbCommand command = SQLServer.CreateCommand();

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

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

반응형