What's the Strangest Corner Case You'Ve Seen in C# or .Net

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

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

So what was T...

Answer: any Nullable<T> - 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<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?");
}

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!

What is the worst gotcha in C# or .NET?


private int myVar;
public int MyVar
{
get { return MyVar; }
}

Blammo. Your app crashes with no stack trace. Happens all the time.

(Notice capital MyVar instead of lowercase myVar in the getter.)

Object.ReferenceEquals prints true for two different objects

This happens because special case is made for constructing empty strings from empty char arrays. The string constructor returns string.Empty for empty strings constructed in this way:

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

From the reference source for string (this is the constructor for a char* parameter):

[System.Security.SecurityCritical]  // auto-generated
private unsafe String CtorCharPtr(char *ptr)
{
if (ptr == null)
return String.Empty;

#if !FEATURE_PAL
if (ptr < (char*)64000)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeStringPtrNotAtom"));
#endif // FEATURE_PAL

Contract.Assert(this == null, "this == null"); // this is the string constructor, we allocate it

try {
int count = wcslen(ptr);
if (count == 0)
return String.Empty;

String result = FastAllocateString(count);
fixed (char *dest = result)
wstrcpy(dest, ptr, count);
return result;
}
catch (NullReferenceException) {
throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
}
}

And also (this is the constructor for a char[] parameter):

    [System.Security.SecuritySafeCritical]  // auto-generated
private String CtorCharArray(char [] value)
{
if (value != null && value.Length != 0) {
String result = FastAllocateString(value.Length);

unsafe {
fixed (char * dest = result, source = value) {
wstrcpy(dest, source, value.Length);
}
}
return result;
}
else
return String.Empty;
}

Note the lines:

        if (count == 0)
return String.Empty;

and

        else
return String.Empty;


Related Topics



Leave a reply



Submit