A while back, I wrote the following in a post:
I continue to be satisfied about the design of C#, but even in our second version, there are a couple of cases in the past few months where we’ve said, “Well, if we’d been thinking about that when we first did the design, that would be a wonderful way to express that concept, but since we didn’t, we’re stuck with what we have now.”
There are two cases that come to mind that I think I’ll talk about.
The first – and I’m not positive that it qualifies based on what I said above, but I’d like to talk about it anyway – is the fact that we have both the .Equals() method and operator==() in the language. This situation gets complicated by the fact that there is both reference equality and value equality (sometimes for the same object, such as string), and it’s hard to come up with a simple scheme that does both. Having said that, this is an area that I don’t think we excelled in.
The second is a somewhat arcane consideration that isn’t especially language-related, but provides a good example of the constraints that timing has.
The Type class (ie what you get when you use typeof(x) or y.GetType()) has a number of members that describe the type. For example, you can find out whether the type is an array, whether it’s a pointer type, etc. In the underlying metadata representation, these attributes are typically encoded into specific bits in the metadata for the type. You can think of this as our primary metadata system, and it was one of the first parts of .NET that was built.
Somewhat later, we created a second metadata system known as Custom Attributes. Rather than being stored as specific bits, custom attributes are stored as blobs of data hung off a piece of metadata. So, it’s puzzle time:
What is the metadata representation of the following:
This is an example of what is know as a “pseduo-custom“ attribute. Serializable looks like an attribute, but instead of being stored as an attribute, it’s stored as a metadata bit, and is accessed through the IsSerializable property on the type object. Which means that if you do typeof(Test).GetCustomAttributes(), you won’t get back a serializable attribute. That can lead to some confusion, and in some cases, the reason we’re in that state is because we didn’t have a full-fledged implementation of attributes early (I’m not sure whether Serializable is one of those cases, but it does demonstrate the issue).
There are two implementations of pseudo-custom attributes. In the first one, the compiler has special-case code for the attribute, and emits the relevant metadata bits. In the second one, the compiler treats it like any other attribute, and then the runtime removes the attribute at sets the metadata bit. The C# compiler uses both approaches (or at least it did at one time).