The OrderedDictionary
overloads the indexing operation so that indexing with an integer N
will get the item in position N
, while indexing with an Object
will retrieve the item coresponding to that object. If one were to create an OrderedDictionary<int, string>
called myDict
, and added items (1,"George") and (0,"Fred") in that order, should myDict[0]
return "George" or "Fred"?
Such an issue could have been resolved by imposing a class constraint on the key type. On the other hand, much of the usefulness of generic collections stems from their ability to efficiently work with value types. Imposing a class constraint on the key type would seem a little ugly.
If the class didn't have to be CLS compliant but merely had to work with vb.net, a sensible design might have been to used named indexed properties. Thus, in the example above, myDict.ByKey[0]
would have yielded "Fred", and myDict.BySequence[0]
would have yielded "George". Unfortunately, languages like C# do not support named indexed properties. While one could have kludged something to allow use of the above syntax even without such properties, the unfortunate decision to wrap the fields of structures like Point
and Rectangle
means that for myDict.ByKey[0] = "Wally"
to work, myDict.ByKey
would have to return a new class object. A struct would be more efficient, but compilers would reject what looked like a write to a read-only structure (notwithstanding that the property wouldn't modify the struct returned by ByKey
, but instead modify the collection to which it holds a reference).
Personally, I think a dictionary-ish object that was specified as keeping track of the insertion order would be a nice thing to have; I'd also like to have a dictionary-ish object which could easily return the key associated with a particular key (so that, e.g. if one has a case-insensitive dictionary and has added a record with a key of "GEORGE", one could ask the dictionary what key is associated with "George" without having to search through all the KeyValuePair
objects returned in an enumeration.
An interface is a contract specifying what its implementer promises to be able to do. It does not need to specify state because state is an implementation detail and only serves to constrain implementers in how this contract is fulfilled. If you want to specify state you might want to rethink you use of interfaces and look at abstract base classes instead.
Best Answer
The fundamental problem with "void" is that it does not mean the same thing as any other return type. "void" means "if this method returns then it returns no value at all." Not null; null is a value. It returns no value whatsoever.
This really messes up the type system. A type system is essentially a system for making logical deductions about what operations are valid on particular values; a void returning method doesn't return a value, so the question "what operations are valid on this thing?" don't make any sense at all. There's no "thing" for there to be an operation on, valid or invalid.
Moreover, this messes up the runtime something fierce. The .NET runtime is an implementation of the Virtual Execution System, which is specified as a stack machine. That is, a virtual machine where the operations are all characterized in terms of their effect on an evaluation stack. (Of course in practice the machine will be implemented on a machine with both stack and registers, but the virtual execution system assumes just a stack.) The effect of a call to a void method is fundamentally different than the effect of a call to a non-void method; a non-void method always puts something on the stack, which might need to be popped off. A void method never puts something on the stack. And therefore the compiler cannot treat void and non-void methods the same in the case where the method's returned value is ignored; if the method is void then there is no return value so there must be no pop.
For all these reasons, "void" is not a type that can be instantiated; it has no values, that's its whole point. It's not convertible to object, and a void returning method can never, ever be treated polymorphically with a non-void-returning method because doing so corrupts the stack!
Thus, void cannot be used as a type argument, which is a shame, as you note. It would be very convenient.
With the benefit of hindsight, it would have been better for all concerned if instead of nothing whatsoever, a void-returning method automatically returned "Unit", a magical singleton reference type. You would then know that every method call puts something on the stack, you would know that every method call returns something that could be assigned to a variable of object type, and of course Unit could be used as a type argument, so there would be no need to have separate Action and Func delegate types. Sadly, that's not the world we're in.
For some more thoughts in this vein see: