Things That Make You Go Hmmm


As you might have gathered from the number of times I make a blog post beginning with “I got the following question from a reader the other day…” I field a lot of questions about the C# language (and in the past fielded a lot of questions about VBScript and JScript.) Each of them is, by definition, about something that was unobvious about the language; if it were obvious, the question wouldn’t have been asked in the first place.

This means that I always learn something when I answer questions. I learn what it is about this language that people find confusing, unobvious, tricky. These are useful data, because we can then use them to make better compiler warnings. We can use them to help us gauge early on what new language features people will find confusing. And so on.

Rather than wait for more questions to come in — though believe me, plenty do — I thought I’d take this opportunity to solicit stories of times when you learned something profoundly non-intuitive about programming languages. Preferably C#, but other languages would be interesting as well. If you’ve got a story to share, please leave a comment on the blog or send me an email. Thanks!

Comments (60)

  1. Nestor Sanchez A. says:

    Hi, Eric.

    My story about non-intuitivity and  how I learned about variance/contravariance when trying to use (or abuse?) C# 2.0 generics…

    I defined a TimeConverter<TTime> class where TTime was the ancestor of TDecade, TYear, TQuarter, TMonth and so on.  The idea was to manage all kind of conversions between the diffrent descendants (i.e. days to quarters, decades to months, etc.)

    But later my surprise was big -and also my disappointment- when discovered that things weren’t so easy.

    I came to your blog precisely investigating about the issue. Fortunately I managed to workaround the problems and things now are clear after reading these long and informative series of the past years.

    Greetings.

  2. Anthony Jones says:

    class Derived : Base

    {

       public Derived(SomeType x)  : base(x)

       { … }

    }

    Took a little while for this syntax to become natural, I’m not sure why.  I kept looking for ways to execute some code in the Derived constructors body where at some point I would expect to call base(x) as if I was overriding a method.  This despite accepting that a constructor on the base class must be called.

    Took me ages to learn that the base constructor must run before any in the derived and I still wish I could at least do some munging on the parameters before letting the base class have them for its constructor.

  3. EricLippert says:

    You can mess around with the parameters to the base ctor:

    public Derived(int x) : base(x + 1) {}

    that’s perfectly legal.

  4. Jesper says:

    My non-obvious story isn’t as clear-cut because I don’t recall the exact circumstances (this was in 2002), but it has to do with static methods/properties (I’ll use "static method" throughout to refer to both). Basically, the first time I tried to put a static method in an interface, it failed, and the first time I tried to override a static method in a derived class, it failed.

    You had a post recently on why this is – it’s because static methods merely are methods for which lookup can be made at compile-time, not class methods. This made me go not just "hmmm" but also something more vivid and unprintable than "hmmm", simply because of the two, I’d rather C# supported class methods. Class methods are very useful tools when it comes to really working with the hierarchy that object orientation in C# gives you; imagine inheriting from an abstract bitmap class and in the process overriding a class property, providing an easily way of checking, for example, what type or extension or color depth is supported by a specific image format. Sometimes class methods can be supplanted by attributes, but far from always — it doesn’t solve any of my two previously mentioned gotchas.

    In my seventh year of using C#, it still boggles my mind. I can see the case for static methods as far as executional expediency goes (no virtual dispatch), but I literally cannot see any other advantages over class methods. I recognize that class methods overtaking static methods at this point would be a compatibility nightmare and that I am likely out of luck, but if you entertain the question as if it was written and considered before C# 1 was ever public, what would your thoughts be on the subject?

  5. Anthony Jones says:

    @Eric,

    Thanks that makes the point well.

    on its own:-

    public Derived(int x) : base(x) {}

    It didn’t occur to me that I could do:-

    public Derived(int x) : base(fn(x)) {}

    I’ve coding in C# for some years and yet I still struggle to grok this.  It could just be me being on the slow side but it just doesn’t seem that obvious.

  6. Jesper says:

    @Anthony: I think the only reason C# has that syntax is because otherwise they’d have to invent a way of calling the initialization part of the constructor (what runs in the constructor and not the creation of the object itself) in regular code. Calling "new" creates the new object and then runs the appropriate constructor. It’s not obvious, but it’s also a special case, because you normally don’t want to separate the two.

  7. Bryan Watts says:

    I was surprised I could not constrain generic types with parameterized constructors.

    After much thought and discussion, I understand why that is the case (generic types describe an interface; construction is an implementation detail).

    Good call on that.

  8. Pavel Minaev says:

    I recall being very surprised when I’ve seen that arrays are covariant in .NET, in any position (I didn’t come from a Java background, so I didn’t realize where that misfeature was coming from, as it’s certainly rather counter to common sense).

  9. Not exactly a "hmm…", but I’ve wondered why languages (especially a new one like C#) don’t go the "extra mile" to guide you (if not practically enforce) accepted "best practices" (and yes, I understand that such guidance could change over time).

    As a specific example: it’s generally agreed that inheritance is overused; given that, why not make the default "sealed" or at least allow "base" to be explicitly used.  Similarly: it’s a lot easier to just inherit from List<Foo> (wrong) than to implement IFoo<List> using List<Foo> (right).

    In somewhat of a similar vein, the D language doesn’t have compiler warnings; the code is either right or wrong.

  10. When I was first learning C, it took me a while to get to grips with pointers. The concept of a memory location that held the address of another memory location made perfect sense to me but the syntax confused me. In C, pointers are usually declared something like "int *x;" suggesting that you’re creating a variable whose type is "int" and whose name is "*x". That’s sort of what you’re doing except not really at all – at this point you haven’t created any variable of type "int". The C++ way of declaring pointers, "int* x;" (suggesting you’re creating a variable of type "int*" whose name is "x") makes much more sense.

  11. RichB says:

    Several years ago, after I’d been a VB programmer for a long time and had already moved to C#, I was surprised to see code using Mid() on the LHS, instead of the RHS. I had no idea a string could be modified in this way.

    [[ ERIC: We deliberately left that out of VBScript. It is deeply weird. ]]

  12. Jon Skeet says:

    I was surprised when I first saw a singleton which looked okay but wasn’t as lazy as I expected. At first I didn’t believe it (this was a newsgroup question, btw – where I’ve learned most of the crazy stuff) but when I tried it, sure enough it failed. These are the two different code snippets:

    public class LazySingleton

    {

       public static final LazySingleton Instance = new LazySingleton();

       static {}

       private LazySingleton() {}    

    }

    public class NonLazySingleton

    {

       public static final NonLazySingleton Instance = new NonLazySingleton();

       private NonLazySingleton() {}    

    }

    The only difference is an empty static constructor. As so often happens, enlightenment came from a mixture of careful spec reading and checking stuff with Reflector.

  13. Rik Hemsley says:

    The fact that using() with object initializers can cause a memory leak is certainly counter-intuitive.

  14. EricLippert says:

    Re: best practices: I would say that C# does do a fairly good job of leading you towards the Pit of Success rather than the Pit of Despair, but of course we can always do better.

    Re: sealed: Yes, as I have stated in this space before, I would have made "sealed" the default.

  15. The yield statement always throws me for a loop.

    Did you intend for the title of the article to be a C+C Music Factory reference?  If so, that’s just bleeding awesome.  If not…

  16. Matt says:

    It’s so long since the first time I touched c# that it’s hard to remember what the Hmmm moments were in 1.0/1.1.

    The behaviour of several operations on unsigned types (specifically the type of the result) being subtly different from C++ making pasting simple code from a C++ example not compile or behave differently.

    That decimal was a floating point rather than fixed point creation.

    That the XmlSerializer had a ‘whitelist’ of blessed primitives it supported and that some *@%$+! decided that DateTime would be in but TimeSpan would be out.

    That you had FileInfo objects but that loads of api’s refused them and required strings instead.

    2.0

    Discovering that the static variables of generic type Foo<t> were per T was interesting (and I see why they are that way) just that it was quite subtle when I first hit it.

    foreach loop variables and their behaviour when pulled into closures still occasionally gets me if I’m not paying attention. I like f#’s way of notifying you of this happening.

    Discovering that the behaviour of Enums under generics was hideous and woefully underperformant (and that fixing it required C++/CLI hackery)

    3.5

    In Linq expressions the select coming at the end. it seems strange given the effort it makes to ‘look like sql’ to put that at the end.

  17. EricLippert says:

    Re: C/C++ pointers:

    Actually "int *x;" is in my opinion the more correct idiom in C++. Read the grammar carefully and you will see that in a local variable declaration, the "int" is the "decl specifier" and the "*x" is the "declarator".

    I also find C/C++ declarations confusing for the same reason. The way I have learned to think about them is to temporarily suspend the natural tendancy to look at it as a variable’s type followed by its name. Rather, the way to conceptualize it is to simly say to yourself "x is some entity which has the property that *x is an integer variable".  

    Similarly, "int x[]" means "x is some entity such that x[y] is an int variable", and so on.

    C# does not suffer from this problem — in C#, the type always goes on the left and the variable’s name goes on the right, end of story. And because all "type modifiers" (?, [], *) in C# go to the right end of the type declaration, there is never a need to parenthesize a type. (In C, is int *x[] a pointer to an array of ints or an array of int pointers? You might have to parenthesize to get the one you want.)

  18. EricLippert says:

    Of course it’s a C&C reference. 🙂

  19. Abraham Tehrani says:

    My story involves the scoping semantics in C#.

    We all know the following will not compile (for good reason), under Java or C#.

           String foo = “goodbye world”;
           {
               String foo = “hello world”;
           }

    However if you move the first declaration of foo below the block, it is a compile time error for C# only.

           {
               String foo = “hello world”;
           }
           String foo = “goodbye world”; // <— Compile error on C#, however this compiles fine under Java.

    If you’re like me, you probably went “Huh??”. Opened up the C# language specification and there it was 8.5.1 “The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs”. According to the specification no matter where the variable declaration occurs in a block it’s scope is for that entire block.

    Java however is different JLS 14.4.2 “The scope of a local variable declaration in a block is the rest of the block in which the declaration appears…”. The JLS is a bit more specific and that is why it will compile cleanly under Java.

    [[ ERIC:  But that’s not the relevant part of the spec. The scope of foo is irrelevant. It is perfectly legal to have two things with the same name in scope at the same time. The relevant part that explains this behaviour is the part that says that everywhere within the nearest enclosing block that a simple name is used, that name must be using unambiguously. That is the part of the spec you are violating here; you are using the simple name “foo” to refer to two completely different things within the contents of the outer block. ]]

  20. Marcel says:

    I remember when I needed to create my first additional Thread in C#. I had seen it in Java (defining some inline classes, implementing Thread classes or Runnable interfaces) and was quite surprised, that I could just pass the name of the thread’s function as a parameter – without any quotationmarks or other symbols. Better than ugly inline classes and better than some kind of string.

    In contrast we have throw new ArgumentNullException("paramName"); Refactoring will not detect this if I rename the parameter.

    If something is wrong with the type of a class i throw an WrongSuperclassException(obj.getType(), typeof(MyClass)), but I cannot do ArgumentNullException(var(paramName)). This felt wrong.

    Well, but as long as C# won’t get any AOP or compile-time attributes this will never be a big issue. Until then we can still use "Strings" for things that are not strings but code.

  21. Jeroen-bart Engelen says:

    Two things come to mind.

    First off in LINQ you can use the equals operator (==) in the ‘where’ bit, but not in the ‘join’ bit. In the ‘join’ bit you need to use the keyword ‘equals’. That just makes LINQ look really inconsistent to me.

    Secondly on my first experience with WCF, I found out the data object serializer makes the XML order dependent. Using ASMX the following XML fragments are treated equal when deserializing:

    <root>

     <firstChild/>

     <secondChild/>

    </root>

    and:

    <root>

     <secondChild/>

     <firstChild/>

    </root>

    In WCF, with the default serializer, they are not. I was always taught that XML should be order independent and that order dependency in an XML document is a bad design.

  22. Thomas says:

    @ Andrew Jenner: "In C, pointers are usually declared something like "int *x;" suggesting that you’re creating a variable whose type is "int" and whose name is "*x". That’s sort of what you’re doing except not really at all – at this point you haven’t created any variable of type "int". The C++ way of declaring pointers, "int* x;" (suggesting you’re creating a variable of type "int*" whose name is "x") makes much more sense."

    There is one very good reason to stick with "int *x;". Namely, if you write:

    int* x, y;

    this suggests that both x and y are pointers, but in fact only x is a pointer!

  23. pete.d says:

    Variable capturing in anonymous methods. I was not surprised that I could use variables from outside the anonymous method inside the method. But I was surprised to find that the variable’s current value wasn’t evaluated for the method, but rather the variable itself was captured.

    Variable capturing is very powerful, and now that I understand it, I would not give up the behavior for anything. But if there’s a way to make it more clear in the language what is going on and how to control the behavior, that would be good.

    That is, make it clear that variables used in an anonymous method are captured, and also make it clear that one can control the instance of the variable used through scoping.

    Do I know off the top of my head how to do that? Not at all. But then that’s your job. 🙂

  24. Ben says:

    If you’re really motivated, you could do worse than take a little trip over to the StackOverflow site. I imagine that there are enough questions there to stop you getting bored.

  25. runefs says:

    public enum someEnum
    {
      first = 1,
      last = 2
    }

    Enum.IsDefined(defaul(someEnum)) == false

    That’s not intuitive to me that a default value is undefined

    [[ ERIC : The default value of an enum is always zero. The design guidelines call out that you should always support a zero value in your enum. ]]

  26. runefs says:

    another one would be that the below code can’t compile:

           int i=5, j;

           long h=10;

           public void Do(){

             j = i/h;

           }

    the type of i/h is resolved to long but a division with integers can never result in a numerical larger result than the first argument. with that in mind int/long will always fit with into an int(?).

  27. Lee says:

    OK my pet peeve is pretty simple, I don’t see why I can’t have the var construct on member variables that are initialised on the declaring line!

    Cheers

    Lee

  28. Mark Rendle says:

    Not about C#, but it happened to me yesterday so it’s fresh in my mind.

    In SQL Server, insert triggers are fired even if no data is actually being inserted into a table. When using a statement of the form "INSERT INTO a SELECT * FROM b", if the SELECT operation returns no rows, the insert trigger still fires but with no rows in the "inserted" table. Took me a while to figure that one out.

  29. Mark says:

    Mine comes from Delphi (which is a bit of a mess syntactically all round). I would expect equality operators to have higher precedence than logical operators but they don’t!

    So the following conditional:

    if a = b and isValid(c) then …

    is interpreted by delphi as

    if (a) = (b and isValid(c)) then …

    rather than the more natural, and more usual:

    if (a = b) and (isValid(c)) then …

    Always throws me.

    And why would you ever want a logical operator in the lvalue or rvalue of an equality? Bizare!

  30. I’ve got a really simple one, which I’ve seen bite several projects: In .NET it’s almost always a mistake not to Dispose an IDisposable – however, the language doesn’t warn you if you do things like fileInfo.OpenText().ReadToEnd() – but doing so will cause non-deterministic "file-locked" failures when done repeatedly.

    In extreme, I’ve seen it cause deeply entreched architectural failures, where new db connections and associated readers are made and passed around without thought of disposal.  Since the use of these connections is not local to the method in this example, refactoring to fix this issue is complicated.

    A compiler warning would be most appreciated!

  31. My first "Hmmm" moment in C# was when I read on Shawn Hargreave’s blog that using enums as Dictionary keys will cause boxing. I didn’t rest until I solved that problem and I found it a rather entertaining challenge, as it allowed me to learn fun stuff about C#.

    My latest "Hmmm" moment in C# was when I tried the following code:

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

    public class UniqueIDAttribute : Attribute

    {

     private static int nextID = 1;

     public int ID { get; private set; }

     public UniqueIDAttribute()

     {

       ID = nextID++;

     }

    }

    public class Base

    {

     [UniqueID]

     public virtual void MyMethod() { }

    }

    public class Derived : Base

    {

     public override void MyMethod()

     {

       base.MyMethod();

     }

    }

    class Program

    {

     static void Main(string[] args)

     {

       UniqueIDAttribute attr;

       attr = (UniqueIDAttribute)Attribute.GetCustomAttribute(typeof(Derived).GetMethod("MyMethod"), typeof(UniqueIDAttribute));

       Console.Write(attr.ID);

       Console.Write(" ");

       attr = (UniqueIDAttribute)Attribute.GetCustomAttribute(typeof(Base).GetMethod("MyMethod"), typeof(UniqueIDAttribute));

       Console.WriteLine(attr.ID);

     }

    }

    Imagine my surprise when the output was "1 2", instead of "1 1" I expected. Of course, once I sat down and thought about it, the reasons behind this became obvious. I still think it would be helpful to have it mentioned somewhere in the documentation 😉

  32. GCS says:

    There was some nuance to foreach statements that left me perplexed for a little while. Specifically, the fact that the compiler can’t really do type checks if either the type of the IEnumerable or the type in the foreach is an interface. For example:

    interface IFoo { … }

    interface IBar { … }

    public static void Main(string[] args)

    {

     IList<IFoo> foos = …;

     foreach (IBar bar in foos) { … }

    }

    This does not throw a compile error, and I expected it would. I eventually looked at the IL and figured out that the foreach gets mapped to an enumerator call and it casts the objects returned from the enumerator to the type specified in the foreach.

  33. Another pet peeve of mine:  the lack of type-safe "unions" ala haskell, and more specifically, the lack of ability to treat exceptions as return values via some union-like construct.

    This is particularly frustrating in the context of LINQ – many methods return a value _usually_ – but throw an exception if anything is out of the ordinary, and being able to cleanly ignore process or delay these exceptions as part of a query would be very useful.

    For instance, consider:

    from inputStr in userInputs

    let inputInt = int.Parse(inputStr) //might break entire enumeration, but int.TryParse isn’t a (decent) option

    Again, this flaw causes misdesigns.  I see lots of API’s that throw exceptions in non-exceptional situations (including many in the base API), and I see lots of API’s that use the value "null" to represent "something unspecified went wrong".  Both of these standard are very unhandy and the cause of deficiencies in error handling – How many do if(file.Exists) file.Open and don’t handle FileNotFound exception, rather than "file.Open catch (FileNotFound…)" – even though the latter is more robust in the face of races?  This is a common scenario and C# drives you more towards the pit of failure than anything else, here…

  34. Jeff Parker says:

    I rather find C# intuitive the one thing I have always had problem with though is a part of the framework. System.DirectoryServices That has to be one of the most confusing things out there. Not only in how it was written but how it works. For Example when ever you create a new directory entry you have to put it in a singleton pattern this is the only way I have figured out to keep it from leaking. A directory entry once create never goes away until the application closes but you put that in something like a windows service and over a week you will have hundreds of thousands of Directory Entries, even if you specifically set them to null and garbage collect them. Then just how you have to work with it in general and how you get things from it. Some active directory object you just can not get there is no definition for them.

    For the most part I find C# and the framework though a really enjoyable experience because it makes sense. Another thing though that is just really wacky is VISTO the tools for office. That has no logic to it.

  35. I have some real horror stories about when C++ compilers first started supporting templates. The compiler messages were truly awful, but each major one (gcc, Borland, MSVC) had their own "dialect", so to speak, and you learned how to decipher the misleading compiler errors into the real problem. This was actually a useful skill back in the day.

    Other things that gave me pause in C++ are mostly declaration syntax regarding CV-qualified pointers and especially function pointers.

    As far as C# goes, I will occasionally find myself treating generics like templates and have to remind myself that they’re runtime-based instead of compile-time. (e.g., passing a variable of generic type to an overloaded function).

    The only other C# catch is that I intuitively expect lambda variable binding to be by-val at the time of the binding (instead of the time of the call), e.g.:

     int tmp1 = 13;

     Func<int> func = () => { return tmp1; };

     tmp1 = 17;

     MessageBox.Show(func().ToString()); // "17"

     MessageBox.Show(tmp1.ToString()); // "17"

    However, this gives rise to a useful idiom:

      object context = new object(); // Usually a class-level variable

      object contextParam = context; // Local variable, just used to pass a copy of the original value to the lambda

      Func<bool> test = () => { return (context == contextParam); };

      MessageBox.Show(test().ToString()); // "True"

      context = new object(); // Sometime later, the class-level variable changes

      MessageBox.Show(test().ToString()); // "False"

    There are a few other "corner cases" regarding boxing and nullable values. And some of the rules regarding "using" could be cleaned up a bit (particularly, allowing "using" a non-IDisposable type). The CLR 2.0 constrained execution regions are downright ugly, but little can be done about that.

    All in all, I must say C# is one of the best designed languages I’ve had the pleasure of working in. I say this as a student of language design (being a fan of Python) as well as a normal programmer.

  36. Craig Stuntz says:

    OK, here is something that I learned about Objective-C the other day. I’m not sure that I like this feature, but it is interesting. Objective-C has both garbage collected and non-garbage collected options. When you use the non-garbage collected option, a peculiar type of duck typing kicks in. any methods that do not have a specific naming pattern (start with "alloc", "new" or "copy") and return objects will auto-release those objects when the current auto release pools goes out of scope.

  37. GregUzelac says:

    This one comes from the WayBackMachine: At my previous job, we maintained language compilers & linkers that were written in FORTRAN.  Subroutine parameters were passed by reference and constants of the same type/value were rolled up into one instance by the linker.

    PROGRAM ALTERCONST

    CALL Foo(FALSE)

    IF (FALSE .EQ. TRUE) THEN

     PRINT *, "What?! FALSE equals TRUE!"

    END IF

    END

    SUBROUTINE Foo(flag)

    BOOL flag

    flag = TRUE

    END

    It took me a while to figure out why the program started misbehaving b/c I assumed constants were, well, constant.

  38. jcoehoorn says:

    This one’s from VB, including recent VB.Net versions.

    Quick: pop question.  How many elements does will this array have:

       Dim MyArray(4) As String

    Correct Answer:  **5**.

  39. Daniel Vargha says:

    In VB6 method ‘Test’ displayed a message box with ‘0’ instead of ‘1’:

    Sub Increment(ByRef x As Integer)

     x = x + 1

    End Sub

    Sub Test

     Dim x as Integer

     x = 0

     Increment (x)

     MsgBox x

    End Sub

    The reason is that in VB6

    1) No parenthesis needed when calling a sub

    2) Putting an extra parenthesis around an expression creates a temporary "variable" to hold the evaluated value for the expression.

    This allowed developers to pass any expression as a ByRef parameter, and the compiler simply ignored the value returned. (Please note that in VB6 parameters were ByRef by default.) In case of a complex expression it is quite obvious that the value cannot be "written back" as the expression is not assignable. However if the expression is purely a variable that IS assignable, I would expect the actual variable to be passed by reference so the method can modify it.

  40. Jason says:

    I’ve never found the ordering of generic contstraints intuitive:

    class SomeClass<T>

       where T: class, SomeBaseClass, IDisposable, new()

    { }

    Why does "class" have to come first, but "new()" have to come last? Why does a concrete class have to come before any interfaces? Why can’t I just put them in any order?

    It’s probably just me, but  I can’t come up with a paradigm so that the ordering makes sense.

  41. xor says:

    feature request: compile time reflection and code templates! i’m always annoyed when i have to implement INotifyPropertyChanged. It is always the same: add a protected virtual method OnPropChanged(string) and make alle property setters call this method with their name as parameter and only if value != old(value).

    i’d like to do this:

    <CodeGen_NotifyPropChanged>

    class DataObject

    {

    }

    and CodeGen_NotifyPropChanged is defined as:

    transform CodeGen_NotifyPropChanged for class

    {

     body(class c)

     {

       foreach(StaticProperty p in c.Properties)

       {

         StaticStatement s = <% OnPropertyChanged(%> StaticString(p.Name) <% ) %>

         p.Setter.AppendBody(s);

       }

     }

    }

    this would append OnPropChanged("Name") to each property setter.

    how to make this work is, again, your job 😉

    but think about the general applicability! for example: Exception processing, logging, transaction management, security, and of course other laborous but uninteresting work.

    i imagine workig with a set of static types that only exist in the compiler, like StaticString, StaticProperty… i think as long as you are constrained to static types in your code template it would be pretty safe.

  42. That events aren’t instantiated, so that you have to do a null check before you call one.  It means you have to use template code every time, and tripped me up when I first used them.

    That generics can’t have an "Enumeration" constraint.

    That generics can’t have a "ThisType" type, so that MyGenericClass.FactoryMethod<ThisType>() could be defined in the baseclass to return whatever the class is.

    That the switch statement in C# isn’t as flexible as the equivalent in VB.NET

  43. Jay Bazuzi says:

    When I first came to C# (from C++) I was totally surprised to find that ‘struct’ and ‘class’ were very different in C#, since they are almost the same in C++.

    More recently I was surprised to find that even apparently immutable structs can change: http://tinyurl.com/dbeosu.  I get it now, but I was very surprised.

     [[ ERIC: In your example, “bar” is a variable, so it should not be surprising that its contents change. That’s what “variable” means. You can put an immutable struct into a mutable variable; if you change what is in the variable, a method called on the variable will of course change its behaviour. All an immutable struct guarantees is that its internals cannot be edited piecemeal in any unit of storage; they have to be edited all together by changing the whole storage, as you’ve done.

    The oddity here is not that immutable structs can change, because they cannot. The oddity is that closures capture variables, not values, which many people find unexpected. ]]

  44. Steve Cooper says:

    **Equality**

    There is something deeply confusing about equality in C# and the .Net framework:

       x == y

    is not always the same as

       x.Equals(y)

    and

       x != y

    is not always the same as

       !(x == y)

    In one class you can override `==`, `!=`, and `Equals(object)`, implement IEquatable and IEquatable(T), and then compare them using an IEqualityComparer(T) or IEqualityComparer.

    The situation is totally screwy.

  45. Ivan Kotev says:

    Not exactly a "Hmmm" moment, but something interesting and it might surprise somebody, but it actually compiles and runs:

    char str[6] = "Hello";

    for (int i = 0; i < 5; i++)

    printf("%c == %cn", str[i], i[str]);

  46. Ivan says:

    It seems that the compiler sums the variables’s address and the offset and it doesn’t matter if their positions are swapped.

  47. ET says:

    "internal protected" semantics got me twice. I wish it had the other meaning: protected AND internal. If I remember correctly IL supports both, why not expose both in C#?

  48. Michael says:

    Ivan: IIRC, a[b] is defined as *(a + b), and since + is commutative, it follows that a[b] == *(a + b) == *(b + a) == b[a]. So yes, it’s confusing but legal.

  49. Weeble says:

    A big revelation for me was when learning Haskell at university, and suddenly understanding why there was such strange syntax for declaring a function type that took multiple arguments:

    multThree :: (Num a) => a -> a -> a -> a  

    multThree x y z = x * y * z

    At first I thought "Why all the "->" arrows? Why aren’t the inputs cleanly separated from the outputs in the type declaration?" I was uncomfortable with this syntax for several lectures until I realised that it’s equivalent to this:

    multThree :: (Num a) => a -> (a -> (a -> a))

    …and you’re defining one-argument functions that return functions! Of course, they got around to explaining this eventually, but they had to cover other stuff first, and all the time I knew there was something that wasn’t quite right about my mental model.

  50. PL-SQL’s Lazy Evaluation can be non-intuitive.  

    SELECT

    r.value random_value

    c.Name

    FROM

    (SELECT random(1000) value FROM DUAL) r,

    Customers  c

    ;

    I would imagine that the subquery would result in a table with only one value, but it turns out every row in the result is has a different random_value!  

  51. In C#, scoping for object initializers can look weird:

    public class SomeClass{
       public string SomeProperty { get; set; }
       public object Clone(
       {
          return new SomeClass { SomeProperty = SomeProperty };
       }
    }

    From an earlier comment by Eric: “The relevant part [of the spec] that explains this behaviour is the part that says that everywhere within the nearest enclosing block that a simple name is used, that name must be using unambiguously.”

    The example above seems to violate this condition. SomeProperty on the LHS refers to the property of the new object. SomeProperty on the RHS refers to what’s scoped in the current method. Wrote a blog article about it here: http://tinyurl.com/b47rta

    [[ ERIC: That does not violate the condition. As I carefully noted, the restriction is on simple names being used inconsistently. The identifier on the left hand side of an object initializer fragment is not a simple name, it is an identifier. There are a few ways you can use things that are not classified as simple names inconsistently; this is one. That said, I agree that this looks weird and I would avoid it. ]] 

     

  52. Kevin Westhead says:

    Evil type coercion in VB is a good one: http://vb.mvps.org/articles/pt199511.pdf

  53. Filini says:

    abstract class AbstractFoo { … }

    class ConcreteFoo : AbstractFoo { … }

    void DoFoo(IList<AbstractFoo> fooList) { … }

    var fooList = new List<ConcreteFoo>();

    DoFoo(fooList ); // COMPILE ERROR

    Then, after 1 minute, it occurs to me that List<AbstractFoo> and List<ConcreteFoo> are two very different objects, and chenged DoFoo to:

    void DoFoo<T>(IList<T> fooList) where T is AbstractFoo

    But at first, in my mind, my implementation looked very intuitive.

  54. OAB says:

    I still don’t understand why you can make an explicit cast from an integer type to a decimal, but not from a floating point to a decimal in C#.

    So:

    decimal d1 = 5;

    is okay, but

    decimal d2 = 5.4;

    is not.

    [[ ERIC: First off, you mean “implicit conversion”, not “explicit cast”. An explicit cast here would be perfectly legal. 

    The reason this is illegal is clearly explained in the C# specification. Briefly: since double and decimal have different ranges and precisions it is possible to lose magnitude when doing the conversion. Therefore the conversion must be explicit, so that it shows up in the source code that this is a dangerous operation that might introduce error.

    In this case the correct thing to do would be to tell the compiler to use a decimal literal. Just say “decimal d = 5.4m;” ]] 

  55. Nick Sharratt says:

    Mines another from the way back, and too far back into the late 80’s for me to recall any exact details.

    It relates to an item of course work I had to do for my degree in Prolog.  It wasn’t the classic AI "Think of an animal" programme, but it wasn’t that complex either.

    The revelation I had was about half way through coding the project, I suddenly realised I’d actually finished – because all of the statements/rules can be read as true in reverse too.

    Ended up being one of the few projects I could hand in early as a result as I’d drastically over estimated how much work it needed.

    I also remember first learning of object oriented programming reading a Turbo Pascal manual, and was amused when I’d finally had the penny drop and said "Ah ha!" out loud to myself, to turn the page (metaphorically – it may have just been the next paragraph) and find it said "When you’ve had your ah-ha moment…" – whoever wrote that knew exactly the right point when they’d explained enough for me to get it.

  56. holatom says:

    I have recently read some article about C# 4.0 and its optional parameters (original article was not written in English so I don’t have link to it here). One thing from article I find very odd:

    If you have something like:

    public class SomeClass

    {

       public static string SaySomething(string s = "rrr")

       {

           return s;

       }

    }

    in one assembly (SomeLib.dll) and something like:

    class Program

    {

       static void Main(string[] args)

       {

           Console.WriteLine(SomeLib.SomeClass.SaySomething());

           Console.WriteLine(SomeLib.SomeClass.SaySomething("abcd"));

       }

    }

    in the second (test.exe). A result of running test.exe is of course "rrr" and "abcd".

    But if you now change SaySomething()’s default parameter value e.g.:

    public static string SaySomething(string s = "rrr changed!!!")

    and then recompile SomeLib.dll (without recompiling test.exe) the result of running test.exe again (with new SomeLib.dll) would be same as in the first case (but not "rrr changed!!!", "abcd" as expected).

    This "feature" of optional parameters implementation is in my opinion undesirable.

  57. nativecpp says:

    Why you can use property as an out param in a function

  58. Simon Buchan says:

    @holatom: options being bound at static link can be very useful:

    public class MyLib

    {

       public const string Version1 = "1.0";

       public const string Version1_1 = "1.1";

       public const string Version1_1_NoMagic = "1.1 (-magic)";

       public static string Latest() { return Version1_1; }

       public MyLib(string version = Version1_1) {…}

    }

    If you want to have optional parameters that can have the default changed later, you have overloading:

    public class Greeter

    {

       public void SayHello() { SayHello("World"); }

       public void SayHello(string name) { … }

    }

  59. runefs says:

    Just revisited my "hmm" int/long is a long. There was a comment that the result actually might need a long

    int.MaxValue/(long)-1 will not fit into an int. however the compiler accepts int x = int.MaxValue/(int)-1 but the result can not be stored in an int. That makes me go hmmm

  60. runefs says:

    That Typebuilder,Methodbuilder and all the other builders violate Liskov Substitution principle

    It seems rather trivial to implement the methods that in the builder version throws an exception like GetConstructors() on Typebuilder.