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