Thursday, 30 March 2017

What's the strangest corner case you've seen in C# or .NET?






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?



Answer



I think I showed you this one before, but I like the fun here - this took some debugging to track down! (the original code was obviously more complex and subtle...)



    static void Foo() 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());
}


So what was T...



Answer: any Nullable - such as int?. All the methods are overridden, except GetType() which can't be; so it is cast (boxed) to object (and hence to null) to call object.GetType()... which calls on null ;-p







Update: the plot thickens... Ayende Rahien threw down a similar challenge on his blog, but with a where T : class, new():



private static void Main() {
CanThisHappen();
}

public static void CanThisHappen() 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?");
}


But it can be defeated! Using the same indirection used by things like remoting; warning - the following is pure evil:



class MyFunnyProxyAttribute : ProxyAttribute {
public override MarshalByRefObject CreateInstance(Type serverType) {
return null;

}
}
[MyFunnyProxy]
class MyFunnyType : ContextBoundObject { }


With this in place, the new() call is redirected to the proxy (MyFunnyProxyAttribute), which returns null. Now go and wash your eyes!


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...