Backdooring Generic Lists through IList
I have a scenario where a class loads objects of one type, due do abstractions I can not use a generic class (generics tend to spread like cancer :) but I often want to work with a generic version of the objects once retrieved, which resulted in code like this (simplified):
List<SomeClass> items = Storage.LoadItems(filename).OfType<SomeClass>().ToList();
Where LoadItems returns a List<object>, then I realized, why not instead have
public void LoadItems(string filename,IList list);
Now I can do this instead
List<SomeClass> items = new List<SomeClass>();
LoadItems(filename,items);
Which should be more efficient. It's also seems a bit more flexible since I can take an existing List and tack on new items. So my questions are, is this a common pattern or do you have a different/better way of achieving this?
I'm also a bit curious that you can do this, if you try and add a object of the wrong type you get an exception, but does that mean that generic lists also do a type check? (which seems a bit unecessary)
EDIT It might actually be a bit more elegant to modif开发者_StackOverflowy the pattern to
public IList LoadItems(string filename,IList list=null);
that way you can use the statement fluently and if no list is passed you could simply instantiate a List<object>
List<T>
implements IList
explicitly.
The implementations cast to T
and call the regular (generic) methods.
Thus, type-checking only happens if you explicitly call the IList
methods.
For example:
void System.Collections.IList.Insert(int index, Object item)
{
ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item);
try {
Insert(index, (T) item);
}
catch (InvalidCastException) {
ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
}
}
It doesn't use the as
keyword because the T
might be a value type.
You can only write as T
if you wrote where T : class
.
Using IList
is fine in most cases; and is certainly faster than using reflection or dynamic
to achieve the same.
Yes it will add a type-check (by virtue of the cast/unbox), but that will not be onerous. If T
is a struct
then you also have some boxing/unboxing, but that too isn't as bad as people fear.
In that scenario, IList
would be fine by me.
Your solution does look sound, there is nothing wrong with it.
Concerning the Add
, it doesn't really do a type check. The code for the Add
you refer to is this:
int System.Collections.IList.Add(Object item)
{
try {
Add((T) item);
}
catch (InvalidCastException) {
throw ...;
}
return Count - 1;
}
It doesn't do a type check; it's just a try/catch.
I like the second approach.
You can pass the IList but then check the type to see if it is a generic list and if so, get generic type and load only records of that type:
Type itemType = typeof (object);
if(list.GetType().GetGenericArguments().Length>0)
{
itemType = list.GetType().GetGenericArguments()[0];
}
for (int i = 0; i < recordCount; i++)
{
if(record.GetType().IsInstanceOfType(itemType))
}
精彩评论