What's the strangest corner case you've se

2018-12-31 08:19发布

I collect a few corner cases and brain teasers and would always like to hear more. The page only really covers C# language bits and bobs, but I also find core .NET things interesting too. For example, here's one which isn't on the page, but which I find incredible:

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

I'd expect that to print False - after all, "new" (with a reference type) always creates a new object, doesn't it? The specs for both C# and the CLI indicate that it should. Well, not in this particular case. It prints True, and has done on every version of the framework I've tested it with. (I haven't tried it on Mono, admittedly...)

Just to be clear, this is only an example of the kind of thing I'm looking for - I wasn't particularly looking for discussion/explanation of this oddity. (It's not the same as normal string interning; in particular, string interning doesn't normally happen when a constructor is called.) I was really asking for similar odd behaviour.

Any other gems lurking out there?

标签: c# .net
30条回答
其实,你不懂
2楼-- · 2018-12-31 08:52

I'm not sure if you'd say this is a Windows Vista/7 oddity or a .Net oddity but it had me scratching my head for a while.

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

In Windows Vista/7 the file will actually be written to C:\Users\<username>\Virtual Store\Program Files\my folder\test.txt

查看更多
看风景的人
3楼-- · 2018-12-31 08:52

Have you ever thought the C# compiler could generate invalid CIL? Run this and you'll get a 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));
  }
}

I don't know how it fares in the C# 4.0 compiler though.

EDIT: this is the output from my system:

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]
查看更多
临风纵饮
4楼-- · 2018-12-31 08:53

Here's one I only found out about recently...

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

The above looks crazy at first glance, but is actually legal.No, really (although I've missed out a key part, but it isn't anything hacky like "add a class called IFoo" or "add a using alias to point IFoo at a class").

See if you can figure out why, then: Who says you can’t instantiate an interface?

查看更多
宁负流年不负卿
5楼-- · 2018-12-31 08:53

I'm arriving a bit late to the party, but I've got three four five:

  1. If you poll InvokeRequired on a control that hasn't been loaded/shown, it will say false - and blow up in your face if you try to change it from another thread (the solution is to reference this.Handle in the creator of the control).

  2. Another one which tripped me up is that given an assembly with:

    enum MyEnum
    {
        Red,
        Blue,
    }
    

    if you calculate MyEnum.Red.ToString() in another assembly, and in between times someone has recompiled your enum to:

    enum MyEnum
    {
        Black,
        Red,
        Blue,
    }
    

    at runtime, you will get "Black".

  3. I had a shared assembly with some handy constants in. My predecessor had left a load of ugly-looking get-only properties, I thought I'd get rid of the clutter and just use public const. I was more than a little surprised when VS compiled them to their values, and not references.

  4. If you implement a new method of an interface from another assembly, but you rebuild referencing the old version of that assembly, you get a TypeLoadException (no implementation of 'NewMethod'), even though you have implemented it (see here).

  5. Dictionary<,>: "The order in which the items are returned is undefined". This is horrible, because it can bite you sometimes, but work others, and if you've just blindly assumed that Dictionary is going to play nice ("why shouldn't it? I thought, List does"), you really have to have your nose in it before you finally start to question your assumption.

查看更多
有味是清欢
6楼-- · 2018-12-31 08:54

Consider this weird case:

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

If Base and Derived are declared in the same assembly, the compiler will make Base::Method virtual and sealed (in the CIL), even though Base doesn't implement the interface.

If Base and Derived are in different assemblies, when compiling the Derived assembly, the compiler won't change the other assembly, so it will introduce a member in Derived that will be an explicit implementation for MyInterface::Method that will just delegate the call to Base::Method.

The compiler has to do this in order to support polymorphic dispatch with regards to the interface, i.e. it has to make that method virtual.

查看更多
还给你的自由
7楼-- · 2018-12-31 08:55

Here is an example of how you can create a struct that causes the error message "Attempted to read or write protected memory. This is often an indication that other memory is corrupt". The difference between success and failure is very subtle.

The following unit test demonstrates the problem.

See if you can work out what went wrong.

    [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);
        }
    }
查看更多
登录 后发表回答