Say you're looking at Collection<T>
and ReadOnlyCollection<T>
in the System.Collections.ObjectModel namespace. You think, “I could use those! In fact, there's a collection I've been meaning to write for awhile:”
class StringCollection : Collection<string> { public StringCollection(params string[] strings) : base(strings) { } public string Join(string separator) { // clearly this is not an efficient solution! string[] strings = new string[Items.Count]; Items.CopyTo(strings, 0); return String.Join(separator, strings); } } class Program { static void Main(string[] args) { StringCollection strings = new StringCollection("one", "two", "three"); Console.WriteLine(strings.Join(", ")); } } // Output: // one, two, three
Perfect! That's exactly what you expected.
Next, you decide to modify your program. You want to add another string:
static void Main(string[] args) { StringCollection strings = new StringCollection("one", "two", "three"); strings.Add("four"); Console.WriteLine(strings.Join(", ")); }
Builds fine and… NotSupportedException Collection is read-only!
But wait, we inherited from Collection<T>
, not ReadOnlyCollection<T>
. What gives?
Well, when we called the base constructor for Collection<T>
, we gave it an IList<string>
. Okay, so really we gave it an Array
of strings, but it was implicitly converted to an IList<string>
. That IList
was stored as a field within the Collection
wrapper (accessible via the protected Items
property).
Calling Add
calls the explicitly implemented interface member IList.Add
on the string[]
which (from the docs:) Throws a not supported exception in all cases.
So, how can we modify the StringCollection
so that it continues to call the appropriate base constructor, but coerces the string[]
to something a bit more useful?
One possibility is to check the IsReadOnly
property of the IList
before passing it along, like so:
class StringCollection : Collection<string> { public StringCollection() : base() { } public StringCollection(params string[] strings) : this((IList<string>) strings) { } public StringCollection(IList<string> strings) : base(strings.IsReadOnly ? new List<string>(strings) : strings) { } // ... }
I'm sure there are better ways, though. Any ideas?
No comments:
Post a Comment