开发者

Backdooring Generic Lists through IList

开发者 https://www.devze.com 2023-01-24 14:08 出处:网络
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 t

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))
   }
0

精彩评论

暂无评论...
验证码 换一张
取 消