Every time I read about one of these bizarre design decisions in .Net, I'm reminded that while Microsoft's slavish devotion to backward compatibility is undeniably a good thing for Operating System APIs, it's really not such a good thing for language and library design.

The Java alternative of "oops. we made a mistake - mark that API as 'deprecated', but don't actually ever remove it" isn't much better, but at least things can eventually get better.

Just the opposite, actually. Subtly changing the behavior in certain cases is the worst solution. The "right" thing to do is to acknowledge that isReadOnly/isFixedSize were poorly thought out, and come up with new API for "this collection has immutable elements", versus "this collection cannot use Add/Remove".

It may be because I come from a background in more loosely-typed languages, but the idea that you can get a different result for the isReadOnly check for the same object, depending on the type of the reference you have to it, makes me itch a bit.

the array type has an explicit implementation of ICollection<T>.IsReadOnly, distinct from the public Array.IsReadOnly getter, that returns true instead of false

I'm reminded that while Microsoft's slavish devotion to backward compatibility is undeniably a good thing for Operating System APIs, it's really not such a good thing for language and library design

You said MS is usually good about backwards compatibility, but that's not a good thing for libraries (of which .NET is) -- in this case, they broke backwards compatibility, which suggests you think this was the right decision. But I guess I misinterpreted what you meant here.

Not really unfathomable, as readers of oldnewthing could confirm. At Microsoft, desire to satisfy the "don't break existing stuff" rule, leads to pretty surprising "bugs". "It's not a bug, it's a feature" really has an important meaning at MS. And some people who pay good $$$ to are thankful for it.

Finally... Doesn't the code look overly complicated for what it does? What would be wrong with e.g.

On an unrelated note... Fuck C#, Java, C, and any other language that results in so much null-checking code (and I wouldn't do it, either, and would further argue that if you do it, you're doing it wrong; these bugs are better found and removed without these null checks in a vast majority of cases; less code always beats more code in this case).

On a related note don't write defensive code if you don't have to. The ctor of ReadOnlyCollection already throws an exception for null - repeatedly checking for null wont change that and the stacktrace is only one line shorter.

Personally I limit null checks to the externally visible API of a library and internal module boundaries.

I find the whole idea of null to be really bizarre in statically-typed languages. It means that every single reference type has an implicit "or null" on it. If you have "Object foo;" that is implicitly "ObjectOrNull foo;". Null doesn't obey any of the API contract set out by Object, so it's really a completely different type, but one that has a value you can assign to just about anything.

I think languages like ML that have an option type get this right. Instead of giving every single reference type an implicit second type, and forcing the programmer to manually check when he only wants the original type, you force reference types to actually hold a reference to an instance of the right type, then force the programmer to manually allow for null in the cases where he actually wants it.

In this case the part I don't get is why they made the change, as opposed to why they kept the old behavior.

The simpler code doesn't handle the second problem I mentioned I was solving: some classes that implement IReadOnlyList<T> are readable but not readonly. I needed a result that wasn't brittle if you started casting it.

Omitting null checks is a great way to increase the time it takes to find out where the heck a null is coming from. Particularly if the value is being stored instead of being used right away. It's not like they take a lot of time to add... alt+enter+enter with resharper. Of course, the lack of non-nullable types is ridiculous.

Omitting null checks is a great way to increase the time it takes to find out where the heck a null is coming from.

Haha, that's true. (Not in this case though; this function seems generic enough that, in absence of non-nullable types (aaaaaargh ;-)), it could return null). Out perspectives are wildly different, though. I believe that value in not having that code (e.g. less cognitive load when maintaining) beats time needed to find the null. But of course, I am speculating. As for the "storing" case, I agree - there, one needs to check for non-null (until we get effin non-nullables, Microsoft, we're talking toyou ;-)!)

when you're writing library code, it's nice to be paranoid like that. you're going to write it once, but that method is going to be used everywhere, so you might as well do a bunch of safety checks.

but yeah, it'd be nice if there was a way to just make it not allow a null to be passed in. it seems to be just about every popular language that has this problem, though. the only one i can think of that i have used that doesn't have that issue is haskell (i have not used other ml-like languages, but i'm assuming they fix the problem too).