322

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?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I like those brain teasers. I think most of them are just interesting and weird edge cases, but the one on capturing variables in anonymous methods is more on the order of Something Everyone Needs To Understand. – Robert Rossney Oct 11 '08 at 20:14
  • 64
    Tested on Mono 2.0 rc; returns True – Marc Gravell Oct 11 '08 at 21:35
  • 1
    Edited to explain that the string example was just that - an example. – Jon Skeet Oct 12 '08 at 18:07
  • i thought .net strings were immutable, and unique by content; did you check object.ReferenceEquals(x,string.Empty)? – Steven A. Lowe Nov 16 '08 at 05:34
  • They're immutable, but not necessarily unique. If you change "new char[0]" to "new char[]{'x'}" in both lines, you end up with references to separate but equal objects. – Jon Skeet Nov 16 '08 at 07:15
  • 1
    There must be some funky optimization at work there... – Doctor Jones Nov 27 '08 at 19:06
  • Yup - optimisation which breaks the specs which say a new object will be allocated. – Jon Skeet Nov 27 '08 at 19:45
  • 10
    both strings end up being string.Empty and it appears that the framework keeps only one reference to that – Adrian Zanescu Jan 14 '09 at 15:49
  • Is there an answer somewhere? – Fowl Feb 02 '09 at 11:04
  • @Fowl: An answer to why the string constructor behaves this way? No, not really. – Jon Skeet Feb 02 '09 at 12:13
  • 34
    It's a memory conservation thing. Look up the MSDN documentation for the static method string.Intern. The CLR maintains a string pool. That's why strings with identical content shows up as references to the same memory i.e. object. – John Leidegren Feb 23 '09 at 12:48
  • I've heard that it is one of the cornerstones of making Strings immutable. – Daniel O Mar 25 '09 at 15:19
  • 12
    @John: String interning only happens automatically for *literals*. That's not the case here. @DanielSwe: Interning isn't *required* for making strings immutable. The fact that it's possible is a nice corollary of immutability, but normal interning isn't happening here anyway. – Jon Skeet Mar 25 '09 at 15:27
  • Well, an empty string is a literal maybe. It is in Delphi – Marco van de Voort May 04 '09 at 09:03
  • @Marco: "" is a string literal. Just creating an empty string in a different way isn't the same thing as a string literal. – Jon Skeet May 04 '09 at 09:25
  • You should add the one from here: http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/1f799cf8d94d1b59/800e2e35ebe3d883 – Marc Gravell Jun 04 '09 at 19:48
  • @Downvoter: Care to comment on why you've downvoted this? – Jon Skeet Jul 24 '09 at 15:14
  • 1
    It might be a bit late now but... Should be community wiki. – finnw Feb 04 '10 at 21:25
  • @finnw: Done. Not sure why it wasn't before... – Jon Skeet Feb 04 '10 at 22:53
  • @opc: I'm getting a dead page for that link :( – Jon Skeet Feb 04 '10 at 22:54
  • 3
    The implementation detail that causes this behavior is explained here: http://blog.liranchen.com/2010/08/brain-teasing-with-strings.html – Liran Sep 14 '10 at 18:00
  • Almost intersting that people don't expect reference to equal, but they expect == to work between two strings. It has to do with Intern and how .Net stores strings. Any string containing the same data is the same object. – Tedd Hansen Jan 24 '11 at 08:03
  • @Tedd: That's simply not true. String *literals* end up in the same object, but you can easily create two distinct string objects with the same textual data. They will still compare as equal using the overloaded == operator so long as both expressions are of type string at compile time, as then the compiler knows to call the overloaded operator. – Jon Skeet Jan 24 '11 at 08:05
  • string a = "Test"; string b = String.IsInterned("Te" + "st"); Debug.WriteLine(object.ReferenceEquals(a, b)); // True – Tedd Hansen Jan 24 '11 at 08:27
  • @Jon - True, you can. But .Net tries to keep one string stored only once - hence the result. String.Intern and String.IsInterned are methods used to located existing strings. – Tedd Hansen Jan 24 '11 at 08:30
  • 1
    @Tedd: I'm not sure what that code is supposed to show. We know that literals are interned, so the unnamed temporary that's passed to IsInterned looks up the previously interned value. However, if it hadn't been in a literal or interned by a call to Intern, the result would have been null. In practice, people rarely call either method, so strings that are generated on the fly (as opposed to literals) are compared character-by-character, not reference-to-reference. – Steven Sudit Jan 24 '11 at 18:34
  • @Tedd: It doesn't "try" to keep one string stored only once. Yes, you *can* call Intern and IsInterned, but they're rarely encountered. – Jon Skeet Jan 24 '11 at 18:42

37 Answers37

394

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!

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 9
    Why can't Nullable.GetType() be defined? Shouldn't the result be typeof(Nullable)? – Drew Noakes Oct 23 '08 at 08:37
  • 69
    Drew: the problem is that GetType() isn't virtual, so it's not overridden - which means that the value is boxed for the method call. The box becomes a null reference, hence the NRE. – Jon Skeet Oct 25 '08 at 19:52
  • 10
    @Drew; additionally, there are special boxing rules for Nullable, which means that an empty Nullable boxes to null, not a box that contains an empty Nullable (and a null un-boxes to an empty Nullable) – Marc Gravell Oct 26 '08 at 08:34
  • Wow, thats pretty surprising to me. Had to play around in a test app to see it for myself. – Frank Schwieterman Jun 30 '09 at 17:52
  • Could you provide a link to the part of the language documentation that explains what "`where T : new()`" means? – finnw Nov 04 '09 at 16:30
  • 6
    Constructor-constraint, 10.1.5 in the C# 3.0 langauge spec – Marc Gravell Nov 04 '09 at 16:39
  • 3
    Maybe Java's type erasure wasn't such a bad idea after all. – finnw Feb 04 '10 at 21:30
  • @finnw: I really hope that was sarcastic. "Yeah, let's make programmer's lives living hell because nullable types would be boxed to null when calling GetType() on an instance using generics." – configurator Sep 09 '10 at 23:47
  • @John Skeet: Would it be correct to say that GetType wouldn't have had to require boxing if Microsoft had every struct automatically shadow Object.GetType with its own implementation, but Microsoft didn't do that, boxing is required to invoke Object.GetType? Would it break anything if in .Net 5.0, Microsoft had structure (or at least nullable types) include such a shadowed implementation? – supercat Apr 16 '11 at 19:29
  • @supercat ; @Jon won't see that unless you remove the "h", but: GetType() itself is not virtual, and changing that would be a huge change. Of course, in many ways there is *no need to ever call it* for structs - the compiler could do the substitution (it already knows); but that would need a spec change. – Marc Gravell Apr 16 '11 at 19:42
  • @Marc Gravell: Will he see your comment? I thought only the first @ tag was effective? I think I see the issue. A struct can implement GetType in such a way as to shadow Object.GetType, and a Nullable probably could too. This would allow one to call GetType on an object which was known to be of type Nullable without boxing. That wouldn't solve the problem in the generic case, though, since method dispatch is determined before the generic type is bound. One may define a structure with a GetType method that returns 1.GetType; if one calls GetType on such a structure... – supercat Apr 16 '11 at 20:30
  • @Marc Gravell: ... the result will be (System.Int32), but if one passes such a structure to a generic function which calls GetType on it, the result will be the structure's type. I guess I find it a little surprising that Nullable doesn't shadow the GetType method to allow it to be used without boxing, and adding a shadowed GetType method to Nullable probably wouldn't break anything, but even if such a shadow method existed wouldn't affect the need to box within a generic function. Thanks for writing. – supercat Apr 16 '11 at 20:33
216

Bankers' Rounding.

This one is not so much a compiler bug or malfunction, but certainly a strange corner case...

The .Net Framework employs a scheme or rounding known as Banker's Rounding.

In Bankers' Rounding the 0.5 numbers are rounded to the nearest even number, so

Math.Round(-0.5) == 0
Math.Round(0.5) == 0
Math.Round(1.5) == 2
Math.Round(2.5) == 2
etc...

This can lead to some unexpected bugs in financial calculations based on the more well known Round-Half-Up rounding.

This is also true of Visual Basic.

Matt J
  • 43,589
  • 7
  • 49
  • 57
Samuel Kim
  • 3,723
  • 2
  • 23
  • 18
  • 22
    It seemed strange to me too. That is, at least, until I had round a big list of numbers and calculate their sum. You then realize that if you simply round up, you will end up with potentially huge difference from the sum of the non-rounded numbers. Very bad if you are doing financial calculations! – Tsvetomir Tsonev Oct 12 '08 at 10:05
  • 6
    Having worked in financial environments I would disagree. If you business requirements need one way of rounding and if your software acts differently, that is where the problems arise. In heavy statistical calculations, Bankers' rounding is more accurate - which is what I think you are referring to. – Samuel Kim Oct 12 '08 at 12:18
  • 255
    In case people didn't know, you can do: Math.Round(x, MidpointRounding.AwayFromZero); To change the rounding scheme. – ICR Oct 12 '08 at 12:24
  • 26
    From the docs: The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction. – ICR Oct 12 '08 at 12:25
  • 8
    I wonder if this is why I see `int(fVal + 0.5)` so often even in languages which have a built-in rounding function. – Ben Blank Feb 24 '09 at 19:50
  • Nice answer! This drove me insane!!! I couldn't figure out why... – Christian Payne Sep 06 '09 at 22:50
  • 32
    Ironically, I worked at a *bank* once and the other programmers started flipping out about this, thinking that rounding was broken in the framework – dan Dec 29 '09 at 20:43
  • 1
    I wrote a stock trade sorting and aggregating app in MS Access years ago (I've done my penance, no need for public humiliation now). If I remember right it did not do banker's rounding and had some nasty side effects due to the fundamental inexactness of real numbers. Ultimately a team and I rewrote the whole thing as a .Net 1.0 ASP.Net app using stored procs. It was a fair bit faster as you might imagine. – Steve Hiner Jan 21 '10 at 08:09
  • @Ben Blank: I use that simply because I want an `int`, not a rounded float. – mpen Mar 01 '10 at 07:19
  • 1
    Yeah! That's one of the reasons I like .NET. Hold on... I don't!!! – victor hugo May 01 '10 at 16:58
  • 1
    This isn't just .net, this goes WAY back to at least VB4. Given this doesn't skew results in most scenarios when aggregating, why isn't this the "normal" way of rounding taught in school? – Jim L May 26 '10 at 17:42
  • 2
    @Jim Leonardo, in school, .5 gets rounded up. In this code, .5 is rounded up if *up* is even, down if *up* is odd. Given the following array `{ 0.5, 1.5, 2.5, 3.5, 4.5, 5.5 }`, rounding would return `{ 0, 2, 2, 4, 4, 6 }`. – Anthony Pegram May 26 '10 at 17:59
  • Note also that the rounding we were taught in schools (or, at least, that I was taught in schools) was to go one more digit than the desired result, and then round. With the IEEE standard rounding (so-called "banker's"), you need to treat a 5 differently if there is an additional remainder. (For example, 2.5 rounds to 2, but 2.5001 rounds to 3). I guess that additional detail was thought to be too much for the 3rd-grade mind. – phoog Dec 22 '10 at 00:44
  • Actually, if you look deeper into this, it has nothing to do with .Net. It the the Intel x86 FPU that uses Banker's rounding. Easy to find in Google. – IamIC Apr 09 '11 at 19:37
  • um.. except you can choose which kind of rounding to use with the second parameter of Math.Round as ICR pointed out. – Tom May 27 '11 at 04:36
176

What will this function do if called as Rec(0) (not under the debugger)?

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

Answer:

  • On 32-bit JIT it should result in a StackOverflowException
  • On 64-bit JIT it should print all the numbers to int.MaxValue

This is because the 64-bit JIT compiler applies tail call optimisation, whereas the 32-bit JIT does not.

Unfortunately I haven't got a 64-bit machine to hand to verify this, but the method does meet all the conditions for tail-call optimisation. If anybody does have one I'd be interested to see if it's true.

Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • 10
    Has to be compiled in release mode, but most definitely works on x64 =) – Neil Williams Oct 12 '08 at 18:43
  • 3
    might be worth updating your answer when VS 2010 comes out since all current JITs will then do the TCO in Release mode – ShuggyCoUk Jun 28 '09 at 23:40
  • 3
    Just tried on VS2010 Beta 1 on 32-bit WinXP. Still get a StackOverflowException. – squillman Aug 26 '09 at 20:45
  • 3
    Yeah, tail call support in the JIT is only useful if the compiler generates tail opcode prefixes, which it looks like the C# compiler still doesn't do. The equivalent F# code should work perfectly, though. :) – bcat Sep 05 '09 at 04:03
  • 130
    +1 for the StackOverflowException – calvinlough Jan 05 '10 at 17:14
  • Doesn't work for me on win7 x64 :( throws stack overflow exception – iBiryukov Apr 22 '10 at 00:45
  • @Ilya - Are you sure you're compiling the application for the Any CPU or x64 platforms? Visual Studio defaults to x86 so even on a 64-bit system you'll see 32-bit behaviour unless you change the compilation setting. – Greg Beech Apr 22 '10 at 00:51
  • @Gred, yeah I am sure :) – iBiryukov Apr 22 '10 at 10:59
  • @Ilya - Perhaps you changed the build target to x64 for Debug mode, then compiled in Release? I made this mistake myself. – Odrade Dec 07 '10 at 18:01
  • 7
    That `++` there totally threw me off. Can't you call `Rec(i + 1)` like a normal person? – configurator Feb 25 '11 at 00:32
  • 1
    @configurator ++i is normal for C style languages. – IamIC Apr 09 '11 at 19:47
  • That `tail call optimization` is interesting: are there similar features in any of the JVMs? Thanks! – java.is.for.desktop Jul 28 '11 at 16:42
  • 1
    @configurator, the difference is that the `i++` operator returns the variable value than increments it, the `++i` increments the value, then returns. and the `++` operator is much more simple than `i + 1`. – Shimmy Weitzhandler Jan 19 '12 at 21:24
  • @Shimmy: How is `++i` simpler than `i+1` ? They are both exactly 3 characters. Further `++i` changes the value of i, which is unnecessary (and potentially confusing) here. All that is required is that the value "one more than i" be passed to the method. – James Curran Feb 16 '12 at 14:43
  • @configurator - I have changed `i++` to `i + 1` because I agree mutating state is poor form in a functional-style method. – Greg Beech Feb 16 '12 at 18:04
  • @JamesCurran, agreed on the part of the unnecessary incremention of local var in current recursion. – Shimmy Weitzhandler Feb 17 '12 at 00:34
111

Assign This!


This is one that I like to ask at parties (which is probably why I don't get invited anymore):

Can you make the following piece of code compile?

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

An easy cheat could be:

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

But the real solution is this:

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

So it's a little know fact that value types (structs) can reassign their this variable.

Omer Mor
  • 5,216
  • 2
  • 34
  • 39
  • 3
    C++ classes can do that too... as I discovered somewhat recently, only to be yelled at for actually trying to use it for an optimization :p – mpen Mar 01 '10 at 07:48
  • 1
    I was using in-place new actually. Just wanted an efficient way to update all fields :) – mpen Mar 13 '10 at 00:06
  • 70
    This is also a cheat: `//this = new Teaser();` :-) – AndrewJacksonZA Mar 18 '10 at 11:06
  • 17
    :-) I'd prefer those cheats in my production code, than this reassignment abomination... – Omer Mor Mar 24 '10 at 21:49
  • 2
    From CLR via C#: The reason they made this is because you can call the parameterless constructor of a struct in another constructor. If you only want to initialize one value of a struct and want the other values to be zero/null (default), you can write `public Foo(int bar){this = new Foo(); specialVar = bar;}`. This is not efficient and not really justified (`specialVar` is assigned twice), but just FYI. (That's the reason given in the book, I don't know why we shouldn't just do `public Foo(int bar) : this()`) – kizzx2 Jan 30 '11 at 17:40
  • @Mark are you really sure you can reassign "this" itself? You are not talking about assigning to "*this" ? – dascandy Jul 28 '11 at 13:45
  • @dascandy: Well you overwrite the chunk of memory that `this` occupies, IIRC. Interpret that how you will. – mpen Jul 28 '11 at 18:35
  • Teaser @this; @this = new Teaser(); // Would also work for a class. – Olivier Jacot-Descombes Nov 12 '11 at 18:56
100

Few years ago, when working on loyality program, we had an issue with the amount of points given to customers. The issue was related to casting/converting double to int.

In code below:

double d = 13.6;

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

does i1 == i2 ?

It turns out that i1 != i2. Because of different rounding policies in Convert and cast operator the actual values are:

i1 == 14
i2 == 13

It's always better to call Math.Ceiling() or Math.Floor() (or Math.Round with MidpointRounding that meets our requirements)

int i1 = Convert.ToInt32( Math.Ceiling(d) );
int i2 = (int) Math.Ceiling(d);
sth
  • 222,467
  • 53
  • 283
  • 367
Jarek Kardas
  • 8,445
  • 1
  • 31
  • 32
74

They should have made 0 an integer even when there's an enum function overload.

I knew C# core team rationale for mapping 0 to enum, but still, it is not as orthogonal as it should be. Example from Npgsql.

Test example:

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");
        }
    }
}
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • 18
    Wow that's a new one for me. Also wierd how ConverTo.ToIn32() works but casting to (int)0 doesn't. And any other number > 0 works. (By "works" I mean call the object overload.) – Lucas Jun 12 '09 at 18:52
  • There is a recommend code analysis rule to enforce good practices around this behavior: http://msdn.microsoft.com/en-us/library/ms182149%28VS.80%29.aspx That link also contains a nice description of how the 0-mapping works. – Chris Clark Dec 16 '09 at 20:14
  • 1
    @Chris Clark: i tried putting None = 0 on enum Symbol. still the compiler chooses enum for 0 and even (int)0 – Michael Buen Dec 17 '09 at 00:51
  • I don't think Chris realized the true point of your post. Has nothing to do with enums as flags. – Jimbo Mar 16 '10 at 19:05
  • This is defined in the spec, and I guess the reason is 0 being the default value for all enums. I'm no complier programmer, but I guess the other option is to initialize all enums to the first value in their list of values. Which would be actually a lot more nonsense. – Gorkem Pacaci Jul 13 '10 at 21:22
  • 2
    IMO they should have introduced a keyword `none` which can be used converted to any enum, and made 0 always an int and not implicitly convertible to an enum. – CodesInChaos Nov 19 '10 at 21:24
  • 5
    ConverTo.ToIn32() works because it's result is no compiletime constant. And only the compiletime constant 0 is convertible to an enum. In earlier versions of .net even only the literal `0` should have been convertible to enum. See Eric Lippert's blog: http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/563282.aspx – CodesInChaos Nov 19 '10 at 21:27
  • It's not about premature optimization. I'm after the language or its pattern's orthogonality and consistency. Indeed, only the constant 0 is convertible to an enum; but if 1 maps to an integer why not for 0? (I know the answer, it has something to do with C language tradition and welcoming programmers from that language camp). – Michael Buen Nov 20 '10 at 16:49
  • Wow.. i didn't knew about that... truly Shocking... Thanx... – Shekhar_Pro Jan 10 '11 at 12:30
  • If you want to call the `object` overload, why are you casting to `int`? `JustTest((object)0)` should call the right thing. – configurator Feb 25 '11 at 00:35
67

This is one of the most unusual i've seen so far (aside from the ones here of course!):

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

It lets you declare it but has no real use, since it will always ask you to wrap whatever class you stuff in the center with another Turtle.

[joke] I guess it's turtles all the way down... [/joke]

RCIX
  • 38,647
  • 50
  • 150
  • 207
  • 34
    You can create instances, though: `class RealTurtle : Turtle { } RealTurtle t = new RealTurtle();` – Marc Gravell Aug 26 '09 at 06:55
  • 24
    Indeed. This is the pattern that Java enums use to great effect. I use it in Protocol Buffers too. – Jon Skeet Aug 26 '09 at 07:09
  • But you can't do that, seeing as RealTurtle is not a Turtle.... – RCIX Aug 26 '09 at 07:56
  • 6
    RCIX, oh yes it is. – Joshua Sep 01 '09 at 16:22
  • 8
    I have used this pattern quite a lot in fancy generics stuff. It allows things like a correctly typed clone, or creating instances of itself. – Lucero Sep 10 '09 at 19:31
  • I dunno. It may not be illegal, but it sure is one of the strangest things i've seen it let me do. – RCIX Sep 20 '09 at 00:47
  • 20
    This is the 'curiously recurring template pattern' http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – porges Nov 05 '09 at 19:30
  • 2
    I second Lucero...use it if you want methods on the base class to return the exact type of the inheritor. – flq Nov 27 '09 at 09:02
  • 1
    This is perfectly valid, and can be very useful to allow the base class to reference the child class's type (for example, [Java Enums](http://madbean.com/2004/mb2004-3/) use it to make sure `compareTo` can only be passed values of that enum). -1... – BlueRaja - Danny Pflughoeft May 26 '10 at 17:51
  • 2
    This is not the 'curiously recurring template pattern' as these are not templates but generics. Not being pedantic. The pattern's uses in C++ do not work with C# generics. – wekempf Nov 01 '10 at 19:10
  • 1
    +1 for the feeble turtle joke. – Tom Chantler Apr 01 '11 at 00:27
  • I actually used this pattern. – bevacqua Jun 16 '11 at 17:18
65

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?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
56

When is a Boolean neither True nor False?

Bill discovered that you can hack a boolean so that if A is True and B is True, (A and B) is False.

Hacked Booleans

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
47

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.

Community
  • 1
  • 1
Benjol
  • 63,995
  • 54
  • 186
  • 268
  • 6
    #2 is an interesting example. Enums are compiler mappings to integral values. So even though you didn't explicitly assign them values, the compiler did, resulting in MyEnum.Red = 0 and MyEnum.Blue = 1. When you added Black, you redefined the value 0 to map from Red to Black. I suspect that the problem would have manifested in other usages as well, such as Serialization. – LBushkin May 25 '09 at 03:02
  • 3
    +1 for Invoke required. At ours' we prefer to explicitly assign values to enums like Red=1,Blue=2 so new one can be inserted before or after it will always result in same value. It is specially necessary if you are saving values to databases. – TheVillageIdiot Jun 04 '09 at 09:40
  • @aman.tur, you might be interested in this question: http://stackoverflow.com/questions/881726/using-hashcode-to-generate-value-for-enum – Benjol Jun 04 '09 at 11:26
  • 53
    I disagree that #5 is an "edge case". Dictionary should not have a defined order based on when you insert values. If you want a defined order, use a List, or use a key that can be sorted in a way that's useful to you, or use an entirely different data structure. – Wedge Jun 26 '09 at 08:30
  • Well, I guess it's not a corner case, but it's definitely a gotcha (was for me anyway) – Benjol Jun 26 '09 at 08:38
  • I spent many hours debugging an InvokeRequired-related bug - very annoying it was. – Tamás Szelei Aug 11 '09 at 08:07
  • 21
    @Wedge, like SortedDictionary perhaps? – Allon Guralnek Aug 22 '09 at 16:54
  • The Dictionary is definitely by design, since it's sorted by hash-code... right? – John Gietzen Sep 04 '09 at 13:57
  • 4
    #3 happens because constants are inserted as literals everywhere they're used (in C#, at least). Your predecessor might have already noticed it, which is why they used the get-only property. However, a readonly variable (as opposed to a const) would work just as well. – Remoun Sep 30 '09 at 09:21
  • #1 is a terrible corner case, which bit us hard. There is an excellent explanation about this here: http://209.85.129.132/search?q=cache:www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html (from Google Cache, because the site seems to be down). It's a very good article, written like a mystery case. – Omer Mor Feb 28 '10 at 09:26
  • #2 [Yet another problem with C# enums](http://stackoverflow.com/questions/2875533/what-features-do-you-want-to-see-in-net-5-c-5/2876193#2876193)... #5 This is how hash-tables work. If you need it to be ordered, use [SortedDictionary](http://msdn.microsoft.com/en-us/library/f7fta44c.aspx) – BlueRaja - Danny Pflughoeft May 26 '10 at 17:55
33

VB.NET, nullables and the ternary operator:

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

This took me some time to debug, since I expected i to contain Nothing.

What does i really contain? 0.

This is surprising but actually "correct" behavior: Nothing in VB.NET is not exactly the same as null in CLR: Nothing can either mean null or default(T) for a value type T, depending on the context. In the above case, If infers Integer as the common type of Nothing and 5, so, in this case, Nothing means 0.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • Interesting enough, I failed to find this answer so I had to create [a question](http://stackoverflow.com/questions/4147277/ternary-operator-vb-vs-c-why-resolves-to-integer-and-not-integer). Well, who knew the answer is in this thread? – GSerg Mar 22 '11 at 15:48
28

I found a second really strange corner case that beats my first one by a long shot.

String.Equals Method (String, String, StringComparison) is not actually side effect free.

I was working on a block of code that had this on a line by itself at the top of some function:

stringvariable1.Equals(stringvariable2, StringComparison.InvariantCultureIgnoreCase);

Removing that line lead to a stack overflow somewhere else in the program.

The code turned out to be installing a handler for what was in essence a BeforeAssemblyLoad event and trying to do

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

By now I shouldn't have to tell you. Using a culture that hasn't been used before in a string comparison causes an assembly load. InvariantCulture is not an exception to this.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • I guess "loading an assembly" is a side effect, since you can observe it with BeforeAssemblyLoad! – Jacob Krall Oct 13 '09 at 20:10
  • 2
    Wow. This is a perfect shot into the maintainer's leg. I guess writing a BeforeAssemblyLoad handler can lead to lots of such surprises. – wigy Feb 01 '11 at 19:05
20

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);
        }
    }
abatishchev
  • 98,240
  • 88
  • 296
  • 433
cbp
  • 25,252
  • 29
  • 125
  • 205
  • My head hurts... Why doesn't this work? – jasonh Jul 13 '09 at 23:44
  • I second this question - can we get an explanation? – SqlRyan Aug 05 '09 at 18:58
  • 2
    Hm i wrote this a few months ago, but I can't remember why exactly it happened. – cbp Aug 06 '09 at 06:50
  • 10
    Looks like a compiler bug; the `+= 500` calls: `ldc.i4 500` (pushes 500 as an Int32), then `call valuetype Program/MyStruct Program/MyStruct::op_Addition(valuetype Program/MyStruct, valuetype [mscorlib]System.Decimal)` - so it then treats as a `decimal` (96-bits) without any conversion. If you use `+= 500M` it gets it right. It simply looks like the compiler *thinks* it can do it one way (presumably due to the implicit int operator) and then decides to do it another way. – Marc Gravell Sep 12 '09 at 10:17
  • The structure is a ValueType so the property is returning a copy. Trying to update a copy of a value type that is going into the bit bucket makes no sense. If you want to update that value, you probably want... MyStruct Temp = bar.Foo; Temp += 500; bar.Foo = Temp; – Bennett Dill Oct 09 '09 at 03:56
  • 1
    Sorry for the double post, here's a more qualified explanation. I'll add this, I have been bit by this and this it sucks, even though I understand why it happens. To me this is an unfortunate limitation of the struct/valuetype. http://bytes.com/topic/net/answers/107771-c-struct-class-can-not-access-properties-struct#post382595 – Bennett Dill Oct 09 '09 at 03:59
  • 2
    @Ben getting compiler errors or the modification not affecting the original struct is fine. An access violation is quite a different beast. The runtime should never throw it if you're just writing safe pure managed code. – CodesInChaos Nov 19 '10 at 21:59
  • @configurator: Fixed in C# 4.0 – Cheng Chen Apr 21 '11 at 03:34
18

C# supports conversions between arrays and lists as long as the arrays are not multidimensional and there is an inheritance relation between the types and the types are reference types

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

Note that this does not work:

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[]'
heijp06
  • 11,558
  • 1
  • 40
  • 60
  • 11
    The IList example is just a cast, because string[] already implements ICloneable, IList, ICollection, IEnumerable, IList, ICollection, and IEnumerable. – Lucas Jun 12 '09 at 18:46
15

This is the strangest I've encountered by accident:

public class DummyObject
{
    public override string ToString()
    {
        return null;
    }
}

Used as follows:

DummyObject obj = new DummyObject();
Console.WriteLine("The text: " + obj.GetType() + " is " + obj);

Will throw a NullReferenceException. Turns out the multiple additions are compiled by the C# compiler to a call to String.Concat(object[]). Prior to .NET 4, there is a bug in just that overload of Concat where the object is checked for null, but not the result of 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;

This is a bug by ECMA-334 §14.7.4:

The binary + operator performs string concatenation when one or both operands are of type string. If an operand of string concatenation is null, an empty string is substituted. Otherwise, any non-string operand is converted to its string representation by invoking the virtual ToString method inherited from type object. If ToString returns null, an empty string is substituted.

Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • 3
    Hmm, but I can imagine this fault as `.ToString` really should never return null, but string.Empty. Nevertheless and error in the framework. – Dykam Mar 01 '10 at 07:44
12

Interesting - when I first looked at that I assumed it was something the C# compiler was checking for, but even if you emit the IL directly to remove any chance of interference it still happens, which means it really is the newobj op-code that's doing the checking.

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

It also equates to true if you check against string.Empty which means this op-code must have special behaviour to intern empty strings.

Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • not to be a smart aleck or anything but have you heard of the [reflector](http://www.red-gate.com/products/reflector/)? it's quite handy in these sorts of cases; – RCIX Jul 13 '09 at 23:29
  • 3
    You're not being smart; you're missing the point - I wanted to generate specific IL for this one case. And anyway, given that Reflection.Emit is trivial for this type of scenario, it's probably as quick as writing a program in C# then opening reflector, finding the binary, finding the method, etc... And I don't even have to leave the IDE to do it. – Greg Beech Jul 14 '09 at 09:35
10

PropertyInfo.SetValue() can assign ints to enums, ints to nullable ints, enums to nullable enums, but not ints to nullable enums.

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

Full description here

Anders Ivner
  • 101
  • 1
  • 2
10
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

The output is "Attempted to read protected memory. This is an indication that other memory is corrupt."

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 1
    Interesting! Sounds like a compiler bug, though; I've ported to C# and it works fine. That said, there are a lot of issues with exceptions thrown in Load, and it behaves differently with/without a debugger - you can catch with a debugger, but not without (in some cases). – Marc Gravell Oct 11 '08 at 21:44
  • Sorry, I forgot, you need to add the combo box to the form before it will. – Joshua Oct 12 '08 at 02:07
  • Is this to do with dialog initialization using an SEH as some kind of horrible internal communication mechanism? I vaguely remember something like that in Win32. – Daniel Earwicker Mar 14 '09 at 17:54
  • No. Attaching an unmanaged debugger revealed it to be a null pointer dereference. – Joshua Mar 14 '09 at 19:53
  • 1
    This is the same problem cbp above. The valuetype being returned is a copy, therefore any references to any properties stemming from said copy are headed to bit-bucket land... http://bytes.com/topic/net/answers/107771-c-struct-class-can-not-access-properties-struct#post382595 – Bennett Dill Oct 09 '09 at 04:04
  • 1
    Nope. There are no structs here. I actually debugged it. It adds a NULL to the list item collection of the native combo box causing a delayed crash. – Joshua Oct 09 '09 at 15:24
10

What if you have a generic class that has methods that could be made ambiguous depending on the type arguments? I ran into this situation recently writing a two-way dictionary. I wanted to write symmetric Get() methods that would return the opposite of whatever argument was passed. Something like this:

class TwoWayRelationship<T1, T2>
{
    public T2 Get(T1 key) { /* ... */ }
    public T1 Get(T2 key) { /* ... */ }
}

All is well good if you make an instance where T1 and T2 are different types:

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

But if T1 and T2 are the same (and probably if one was a subclass of another), it's a compiler error:

var r2 = new TwoWayRelationship<int, int>();
r2.Get(1);  // "The call is ambiguous..."

Interestingly, all other methods in the second case are still usable; it's only calls to the now-ambiguous method that causes a compiler error. Interesting case, if a little unlikely and obscure.

tclem
  • 828
  • 1
  • 7
  • 9
10

C# Accessibility Puzzler


The following derived class is accessing a private field from its base class, and the compiler silently looks to the other side:

public class Derived : Base
{
    public int BrokenAccess()
    {
        return base.m_basePrivateField;
    }
}

The field is indeed private:

private int m_basePrivateField = 0;

Care to guess how we can make such code compile?

.

.

.

.

.

.

.

Answer


The trick is to declare Derived as an inner class of Base:

public class Base
{
    private int m_basePrivateField = 0;

    public class Derived : Base
    {
        public int BrokenAccess()
        {
            return base.m_basePrivateField;
        }
    }
}

Inner classes are given full access to the outer class members. In this case the inner class also happens to derive from the outer class. This allows us to "break" the encapsulation of private members.

Community
  • 1
  • 1
Omer Mor
  • 5,216
  • 2
  • 34
  • 39
  • That actually is well-documented; http://msdn.microsoft.com/en-us/library/ms173120%28VS.80%29.aspx. It can be a useful feature at times, especially if the outer class is static. –  Oct 20 '10 at 00:02
  • Yes - of course it's documented. However, very few people solved this puzzle so I thought it's a cool piece of trivia. – Omer Mor Oct 20 '10 at 07:51
  • 2
    Seems like you'd have a very strong possibility of a stack overflow by having an inner class inherit its owner... – Jamie Treworgy Oct 28 '10 at 16:58
  • Yet another similar (and perfectly correct) case is that an object can access a private member of another object of the same type: `class A { private int _i; public void foo(A other) { int res = other._i; } }` – Olivier Jacot-Descombes Nov 12 '11 at 20:02
10

Just found a nice little thing today:

public class Base
{
   public virtual void Initialize(dynamic stuff) { 
   //...
   }
}
public class Derived:Base
{
   public override void Initialize(dynamic stuff) {
   base.Initialize(stuff);
   //...
   }
}

This throws compile error.

The call to method 'Initialize' needs to be dynamically dispatched, but cannot be because it is part of a base access expression. Consider casting the dynamic arguments or eliminating the base access.

If I write base.Initialize(stuff as object); it works perfectly, however this seems to be a "magic word" here, since it does exactly the same, everything is still recieved as dynamic...

TDaver
  • 7,164
  • 5
  • 47
  • 94
8

In an API we're using, methods that return a domain object might return a special "null object". In the implementation of this, the comparison operator and the Equals() method are overridden to return true if it is compared with null.

So a user of this API might have some code like this:

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

or perhaps a bit more verbose, like this:

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

where GetDefault() is a method returning some default value that we want to use instead of null. The surprise hit me when I was using ReSharper and following it's recommendation to rewrite either of this to the following:

return test ?? GetDefault();

If the test object is a null object returned from the API instead of a proper null, the behavior of the code has now changed, as the null coalescing operator actually checks for null, not running operator= or Equals().

Tor Livar
  • 1,193
  • 1
  • 9
  • 9
  • 1
    not really a c# corner case, but dear lord who thought that up?!? – Ray Booysen Jun 22 '10 at 08:03
  • Isn't this code just using nullable types? Hence ReSharper recommending the "??" use. As Ray said, I wouldn't have thought this a corner case; or am I wrong? – Tony Jun 22 '10 at 14:14
  • 1
    Yes, the types are nullable - and there is a NullObject in addition. If it is a corner case, I don't know, but at least it is a case where 'if (a != null) return a; return b;' is not the same as 'return a ?? b'. I absolutely agree it is a problem with the framework/API design - overloading == null to return true on an object is certainly not at good idea! – Tor Livar Jun 22 '10 at 14:47
8

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.

Jordão
  • 55,340
  • 13
  • 112
  • 144
  • That does indeed sound odd. Will have to investigate later :) – Jon Skeet Jan 05 '11 at 14:16
  • @Jon Skeet: I found this while researching [implementation strategies](http://codecrafter.blogspot.com/2010/10/roles-in-c-implementation.html) for [roles in C#](http://codecrafter.blogspot.com/2010/10/roles-in-c.html). Would be great to get your feedback on that one! – Jordão Jan 05 '11 at 17:19
7

The following might be general knowledge I was just simply lacking, but eh. Some time ago, we had a bug case which included virtual properties. Abstracting the context a bit, consider the following code, and apply breakpoint to specified area :

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

While in the Derived object context, you can get the same behavior when adding base.Property as a watch, or typing base.Property into the quickwatch.

Took me some time to realize what was going on. In the end I was enlightened by the Quickwatch. When going into the Quickwatch and exploring the Derived object d (or from the object's context, this) and selecting the field base, the edit field on top of the Quickwatch displays the following cast:

((TestProject1.Base)(d))

Which means that if base is replaced as such, the call would be

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

for the Watches, Quickwatch and the debugging mouse-over tooltips, and it would then make sense for it to display "AWESOME" instead of "BASE_AWESOME" when considering polymorphism. I'm still unsure why it would transform it into a cast, one hypothesis is that call might not be available from those modules' context, and only callvirt.

Anyhow, that obviously doesn't alter anything in terms of functionality, Derived.BaseProperty will still really return "BASE_AWESOME", and thus this was not the root of our bug at work, simply a confusing component. I did however find it interesting how it could mislead developpers which would be unaware of that fact during their debug sessions, specially if Base is not exposed in your project but rather referenced as a 3rd party DLL, resulting in Devs just saying :

"Oi, wait..what ? omg that DLL is like, ..doing something funny"

Dynami Le Savard
  • 4,986
  • 2
  • 26
  • 22
7

This one's pretty hard to top. I ran into it while I was trying to build a RealProxy implementation that truly supports Begin/EndInvoke (thanks MS for making this impossible to do without horrible hacks). This example is basically a bug in the CLR, the unmanaged code path for BeginInvoke doesn't validate that the return message from RealProxy.PrivateInvoke (and my Invoke override) is returning an instance of an IAsyncResult. Once it's returned, the CLR gets incredibly confused and loses any idea of whats going on, as demonstrated by the tests at the bottom.

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

Output:

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) 
Steve
  • 438
  • 3
  • 10
6

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

Spencer Ruport
  • 34,865
  • 12
  • 85
  • 147
  • 2
    This is indeed a vista (not 7, afaik) security enhancement. But the cool thing is that you can read and open the file with the program files path, while if you look there with explorer there is nothing. This one took me almost a day of work @ a customer before I finally found it out. – Henri Mar 11 '10 at 22:02
  • It's definitely a Windows 7 thing too. That's what I was using when I ran into it. I understand the reasoning behind it but it was still frustrating to figure out. – Spencer Ruport Mar 12 '10 at 04:35
  • In Vista/Win 7 (winXP technically too) apps should write to an AppData folder in Users-folder land, as its technically user data. Applications shouldn't write to programfiles/windows/system32/etc ever, unless they have admin priveledges, and those priveledges should only be there to say upgrade the program/uninstall it/install new feature. BUT! Still don't write to system32/windows/etc :) If you ran that code above as admin (right click > run as admin) it should theoretically write to the program files app folder. – Steve Syfuhs Mar 31 '10 at 22:15
  • Sounds like Virtualization - http://crispybit.spaces.live.com/blog/cns!1B71C2122AD43308!134.entry – Colin Newell Jun 01 '10 at 10:51
6

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]
Jordão
  • 55,340
  • 13
  • 112
  • 144
3

There is something really exciting about C#, the way it handles closures.

Instead of copying the stack variable values to the closure free variable, it does that preprocessor magic wrapping all occurences of the variable into an object and thus moves it out of stack - straight to the heap! :)

I guess, that makes C# even more functionally-complete (or lambda-complete huh)) language than ML itself (which uses stack value copying AFAIK). F# has that feature too, as C# does.

That does bring much delight to me, thank you MS guys!

It's not an oddity or corner case though... but something really unexpected from a stack-based VM language :)

Bubba88
  • 1,910
  • 20
  • 44
3

From a question I asked not long ago:

Conditional operator cannot cast implicitly?

Given:

Bool aBoolValue;

Where aBoolValue is assigned either True or False;

The following will not compile:

Byte aByteValue = aBoolValue ? 1 : 0;

But this would:

Int anIntValue = aBoolValue ? 1 : 0;

The answer provided is pretty good too.

Community
  • 1
  • 1
MPelletier
  • 16,256
  • 15
  • 86
  • 137
  • although I`ve not test it I`m sure that this will work: Byte aByteValue = aBoolValue ? (Byte)1 :(Byte) 0; Or: Byte aByteValue =(Byte)( aBoolValue ? 1 : 0); – Alex Pacurar May 10 '10 at 12:59
  • 2
    Yes, Alex, that would work. The key is in the implicit casting. `1 : 0` alone will implicitly cast to int, not Byte. – MPelletier May 14 '10 at 03:07
2

The scoping in c# is truly bizarre at times. Lets me give you one example:

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

OleDbCommand command = SQLServer.CreateCommand();

This fails to compile, because command is redeclared? There are some interested guesswork as to why it works that way in this thread on stackoverflow and in my blog.

Community
  • 1
  • 1
Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53
  • 34
    I don't view that as particularly bizarre. What you call "perfectly correct code" in your blog is perfectly *incorrect* according to the language specification. It may be correct in some imaginary language you'd *like* C# to be, but the language spec is quite clear that in C# it's invalid. – Jon Skeet Jun 26 '09 at 08:48
  • 7
    Well it is valid in C/C++. And since it is C# I would have liked it to still work. What bugs me the most is that there is no reason for the compiler to do this. It's not like it hard to do nested scoping. I guess it all comes down to the element of least suprise. Meaning that it can be that the spec says this and that, but that doesn't really help me very much if it's completely illogical that it behaves that way. – Anders Rune Jensen Jun 26 '09 at 09:08
  • 6
    C# != C/C++. Would you also like to use cout << "Hello World!" << endl; instead of Console.WriteLine("Hello World!");? Also it not illogical, just read the spec. – Kredns Jun 28 '09 at 18:32
  • 9
    I am speaking about scoping rules which is part of the core of the language. You are speaking about the standard library. But it's now clear to me that I should simply read the tiny specification of c# language before I start programming in it. – Anders Rune Jensen Jun 29 '09 at 16:11
  • 6
    Eric Lippert actually posted the reasons why C# is designed like that recently: http://blogs.msdn.com/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx. The summary is because it's less likely that changes will have unintended consequences. – Helephant Nov 25 '09 at 17:16
  • 1
    Thanks. That sheds quite a lot of light on it. But really his examples are far fetched to me. Example 1 would simply give a warning in C++. I don't really see they problem. It's also sloppy programming practice to write such big function so I don't really see the point in bending over the language just to fit people who can't write proper code ;-) – Anders Rune Jensen Nov 25 '09 at 20:49
2

The following doesn't work:

if (something)
    doit();
else
    var v = 1 + 2;

But this works:

if (something)
    doit();
else {
    var v = 1 + 2;
}
Anders Rune Jensen
  • 3,758
  • 2
  • 42
  • 53
  • 12
    I don't see how it is a corner case... In the first example, there is no way you can use the `v` variable, since its scope is the `else` block and you can only have one instruction in it if you don't put braces – Thomas Levesque Aug 10 '10 at 15:44
  • i don't see the difference of the two code snippet. – Benny Aug 11 '10 at 06:22
  • @Thomas: Yes, but why is that an error? I might have wanted to add the statement just to be able to break in the else clause. In C++ this is perfectly valid. I find it discomforting that there is a semantic difference between else {} and else when there is only one statement in the clause. – Anders Rune Jensen Aug 11 '10 at 08:39
  • 1
    @Anders: Your answers put a lot of focus on the fact that C# differs from C++ like here: http://stackoverflow.com/questions/194484/whats-the-strangest-corner-case-youve-seen-in-c-or-net/1047948#1047948 This thread isn't about the differences between C# and C++. An edge case in C# isn't a difference from C++. Others have noted you will find answers in the C# spec. – John K Aug 11 '10 at 13:04
  • @jdk: I added the C++ note for completeness. I agree that it might not be the biggest edge case I have seen, it just suprised me when I found it yesterday. – Anders Rune Jensen Aug 11 '10 at 19:18
  • After a bit of reflection I can see that this will help people new to programming and with almost no harm for normal programmers. So instead of calling it a corner case, one might call it a feature :-) – Anders Rune Jensen Aug 18 '10 at 09:40
2

here are a few of mine:

  1. this can be null when calling an instance method with out a NullReferenceException being thrown
  2. a default enumeration value doesn't have to be defined for the enumeration

Simple one first: enum NoZero { Number = 1 }

        public bool ReturnsFalse()
        {
            //The default value is not defined!
            return Enum.IsDefined(typeof (NoZero), default(NoZero));
        }

The below code can actually print true!

 internal sealed class Strange
{
    public void Foo()
    {
        Console.WriteLine(this == null);
    }
}

A simple piece of client code that will result in that is delegate void HelloDelegate(Strange bar);

public class Program
{
    [STAThread()]
    public static void Main(string[] args)
    {
        Strange bar = null;
        var hello = new DynamicMethod("ThisIsNull",
            typeof(void), new[] { typeof(Strange) },
         typeof(Strange).Module);
        ILGenerator il = hello.GetILGenerator(256);
        il.Emit(OpCodes.Ldarg_0);
        var foo = typeof(Strange).GetMethod("Foo");
        il.Emit(OpCodes.Call, foo);
        il.Emit(OpCodes.Ret);
        var print = (HelloDelegate)hello.CreateDelegate(typeof(HelloDelegate));
        print(bar);
        Console.ReadLine();
    }
}

this is actually true in most languages as long as the instance method when called doesn't use the state of the object. this is only dereferenced when the state of the object is accessed

Rune FS
  • 21,497
  • 7
  • 62
  • 96
  • 4
    The enum case isn't actually surprising, the default underlaying type of an enum is `int`, so default of the enum will return 0, which is quite undefined in NoZero indeed. Even by specifying a custom type (within `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, or `ulong`) to your enum, the default value of all those type is still 0. – Dynami Le Savard Oct 14 '10 at 16:35
  • @Dynami yes it's because of the default value of the underlying type but (to me) it's rather senseless to have an invalid default value for a valuetype it's kind of having (1,-1) as default for int. The value simply makes no sense in the context of the given type – Rune FS Oct 14 '10 at 17:40
2

This one is pretty straightforward but I still find it somewhat interesting. What would be the value of x after the call to Foo?

static int x = 0;

public static void Foo()
{
    try { return; }
    finally { x = 1; }
}

static void Main() { Foo(); }
1

If you have the extension method:

public static bool? ToBoolean(this string s)
{
    bool result;

    if (bool.TryParse(s, out result))
        return result;
    else
        return null;
}

and this code:

string nullStr = null;
var res = nullStr.ToBoolean();

This will not throw an exception because it is an extension method (and really HelperClass.ToBoolean(null)) and not an instance method. This can be confusing.

Lasse Espeholt
  • 17,622
  • 5
  • 63
  • 99
  • I don't think this is a strange corner-case, more run of the mill syntax design. This behaviour allows you to do things like `static void IfNotNull(Action action)`... If your extension method has a problem with a null `this` parameter then throw an `ArgumentNullException`. – Keith Aug 22 '10 at 21:39
  • @Keith It can certainly be useful but when you look at it (from a Java, C++, C# 2 perspective) it will be a strange thing and as a C# 3+ developer you would still have to check whether this is indeed a extension method (not on strings, but on more advanced examples) and not an instance method where they (others code) forgot a null-check. – Lasse Espeholt Aug 23 '10 at 04:25
  • 1
    I guess my point is that the extension method way of working is better in all the places where you would use one rather than an instance method. Take your example method: it returns a `bool?` - it's quite acceptable (even preferred) for your `nullStr.ToBoolean()` to return `null`, rather than have it throw a `NullReferenceException` – Keith Aug 23 '10 at 12:57
  • 1
    I think he's saying if you were inheriting code, and saw the snippet without knowing the extension method definition, it would be confusing. – Michael Blackburn Apr 03 '11 at 04:30
  • I think they should have used another symbol. Like piping in F#. `nullStr|>ToBoolean` or `nullStr->ToBoolean`. – Lasse Espeholt Apr 03 '11 at 18:31
-4

The following prints False instead of throwing an overflow exception:

Console.WriteLine("{0}", yep(int.MaxValue ));


private bool yep( int val )
{
    return ( 0 < val * 2);
}
Craig Eddy
  • 889
  • 1
  • 6
  • 17
  • 2
    You can have your OverflowException by wrapping the test in checked{}, or setting the appropriate compiler option. It's not immediately obvious why the default is unchecked... http://msdn.microsoft.com/en-us/library/khy08726(VS.71).aspx – stevemegson Nov 05 '08 at 22:53
  • 7
    The default is unchecked because the performance hit for doing this check on every integer operation in code is expensive. – Peter Oehlert Dec 23 '08 at 17:38
  • Also, the default for VB is to have it all checked. C# compiler team made a different choice for their default trying to more closely what their target audience would expect. – Peter Oehlert Dec 23 '08 at 17:39
  • 9
    int.MaxValue * 2 is a negative number in unchecked arithmetic, which is the default in C#, there for the comparison returns false. This is not unexpected behavior :P – Lucas Jun 12 '09 at 18:38
-4

This one had me truly puzzled (I apologise for the length but it's WinForm). I posted it in the newsgroups a while back.

I've come across an interesting bug. I have workarounds but i'd like to know the root of the problem. I've stripped it down into a short file and hope someone might have an idea about what's going on.

It's a simple program that loads a control onto a form and binds "Foo" against a combobox ("SelectedItem") for it's "Bar" property and a datetimepicker ("Value") for it's "DateTime" property. The DateTimePicker.Visible value is set to false. Once it's loaded up, select the combobox and then attempt to deselect it by selecting the checkbox. This is rendered impossible by the combobox retaining the focus, you cannot even close the form, such is it's grasp on the focus.

I have found three ways of fixing this problem.

a) Remove the binding to Bar (a bit obvious)

b) Remove the binding to DateTime

c) Make the DateTimePicker visible !?!

I'm currently running Win2k. And .NET 2.00, I think 1.1 has the same problem. Code is below.

using System;
using System.Collections;
using System.Windows.Forms;

namespace WindowsApplication6
{
    public class Bar
    {
        public Bar()
        {
        }
    }

    public class Foo
    {
        private Bar m_Bar = new Bar();
        private DateTime m_DateTime = DateTime.Now;

        public Foo()
        {
        }

        public Bar Bar
        {
            get
            {
                return m_Bar;
            }
            set
            {
                m_Bar = value;
            }
        }

        public DateTime DateTime
        {
            get
            {
                return m_DateTime;
            }
            set
            {
                m_DateTime = value;
            }
        }
    }

    public class TestBugControl : UserControl
    {
        public TestBugControl()
        {
            InitializeComponent();
        }

        public void InitializeData(IList types)
        {
            this.cBoxType.DataSource = types;
        }

        public void BindFoo(Foo foo)
        {
            this.cBoxType.DataBindings.Add("SelectedItem", foo, "Bar");
            this.dtStart.DataBindings.Add("Value", foo, "DateTime");
        }

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.checkBox1 = new System.Windows.Forms.CheckBox();
            this.cBoxType = new System.Windows.Forms.ComboBox();
            this.dtStart = new System.Windows.Forms.DateTimePicker();
            this.SuspendLayout();
            //
            // checkBox1
            //
            this.checkBox1.AutoSize = true;
            this.checkBox1.Location = new System.Drawing.Point(14, 5);
            this.checkBox1.Name = "checkBox1";
            this.checkBox1.Size = new System.Drawing.Size(97, 20);
            this.checkBox1.TabIndex = 0;
            this.checkBox1.Text = "checkBox1";
            this.checkBox1.UseVisualStyleBackColor = true;
            //
            // cBoxType
            //
            this.cBoxType.FormattingEnabled = true;
            this.cBoxType.Location = new System.Drawing.Point(117, 3);
            this.cBoxType.Name = "cBoxType";
            this.cBoxType.Size = new System.Drawing.Size(165, 24);
            this.cBoxType.TabIndex = 1;
            //
            // dtStart
            //
            this.dtStart.Location = new System.Drawing.Point(117, 40);
            this.dtStart.Name = "dtStart";
            this.dtStart.Size = new System.Drawing.Size(165, 23);
            this.dtStart.TabIndex = 2;
            this.dtStart.Visible = false;
            //
            // TestBugControl
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.dtStart);
            this.Controls.Add(this.cBoxType);
            this.Controls.Add(this.checkBox1);
            this.Font = new System.Drawing.Font("Verdana", 9.75F,
            System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
            ((byte)(0)));
            this.Margin = new System.Windows.Forms.Padding(4);
            this.Name = "TestBugControl";
            this.Size = new System.Drawing.Size(285, 66);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.CheckBox checkBox1;
        private System.Windows.Forms.ComboBox cBoxType;
        private System.Windows.Forms.DateTimePicker dtStart;
    }

    public class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Load += new EventHandler(Form1_Load);
        }

        void Form1_Load(object sender, EventArgs e)
        {
            InitializeControl();
        }

        public void InitializeControl()
        {
            TestBugControl control = new TestBugControl();
            IList list = new ArrayList();
            for (int i = 0; i < 10; i++)
            {
                list.Add(new Bar());
            }
            control.InitializeData(list);
            control.BindFoo(new Foo());
            this.Controls.Add(control);
        }

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "Form1";
        }

        #endregion
    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
Quibblesome
  • 25,225
  • 10
  • 61
  • 100
-18

I think the answer to the question is because .net uses string interning something that might cause equal strings to point to the same object (since a strings are mutable this is not a problem)

(I'm not talking about the overridden equality operator on the string class)

James Z
  • 61
  • 4
  • 15
    Strings are *immutable*, not mutable. And this isn't "normal" string interning - it only occurs when you pass in an empty char array. However, the question isn't really "why does this happen?" but "what similar things have you seen?" – Jon Skeet Oct 12 '08 at 18:04
  • 10
    Reminds me of how any discussion of the Fizz Buzz problem leads to at least half the responses being solutions of the problem. – Wedge Oct 14 '08 at 01:41
  • 2
    Half of which were incorrect. – Joe Aug 20 '10 at 20:24