1

Hullo,

For some reason (that I can't work out) a function that returns an ilist keeps returning null when I call it (even though earlier in the same method it returns an empty ilist, but whatever) so I was making a function to take in several ilists and then output the total number of elements across all the lists (ignoring the null ones as to avoid an exception).

So far I have:

private int TotalItemsInLists(params IList[] lists)
    {
        int total = 0;


        foreach (IList list in lists)
            if (list != null)
                total += list.Count;

        return total;
    }

but that causes problems in that params IList[] lists doesn't seem to be called correctly, as the ILists are all lists of different types.

Anyone know how I can remedy this problem?

Thanks, Harry

Edit: Here is the code that calls it

private bool TooManyItems(Person person)
    {
        return TotalItemsInLists(jobService.BeingDevelopedBy(person), jobService.BeingTestedBy(person),
                                 jobService.BeingFixedBy(person), quoteService.BeingProducedBy(person),
                                 ticketService.BeingActivelyHandledBy(person),
                                 ticketService.BeingTestedBy(person),
                                 ticketService.AllAvailable(person)) > 10;
    }

And here's an example of one of the methods they call (they're all pretty similar, just different databases and filters)

public IList<Ticket> BeingActivelyHandledBy(Person person)
    {
        return (from ticket in Query()
                where ticket.Handler == person
                      && ticket.Status == TicketStatus.Open
                      && ticket.Release == null
                select ticket).ToList();
    }
7
  • 1
    Could you post the call to your method Commented Mar 14, 2011 at 10:16
  • Can you provide a code example and the exptected result? Commented Mar 14, 2011 at 10:16
  • Lists that you're passing as parameters are declared as generic? I mean List<User> userList = new List<User>();? Commented Mar 14, 2011 at 10:17
  • There's the code to explain it better, sorry Commented Mar 14, 2011 at 10:20
  • What does "doesn't seem to be called correctly" mean? Commented Mar 14, 2011 at 10:22

5 Answers 5

1

You have to cast the input to IList:

TotalItemsInLists((IList)jobService.BeingDevelopedBy(person), ...
Sign up to request clarification or add additional context in comments.

3 Comments

There isn't any guarantee that the lists will even implement the non-generic IList. IList<T> guarantees ICollection<T>, IEnumerable<T> and IEnumerable, and that's it.
true, I think Øyvind's proposal to return List<T> instead of IList<T> is the best answer
That doesn't address the problem at all, which is that the method is being given multiple instances of IList<T> where T is different, thus the solution is to cast them down to a non-generic interface, the only one of which that is guaranteed to work is IEnumerable.
1

I think the only thing you can do is change the method to accept the non-generic IEnumerable type.

private int TotalItemsInLists(params IEnumerable[] lists)
{
    int total = 0;

    foreach (var list in lists)
        if (list != null)
            total += list.Count();

    return total;
}

If performance becomes a problem, you can do a quick check in the loop if the type is also a generic or non-generic ICollection and read its Count property directly.

3 Comments

Why doing if (list != null)? list.Count() is an extension method and doesn't need this extra if.
If source is null, Count() throws an ArgumentNullException
Even though it isn't a requirement for extension methods, all the Linq extension methods throw ArgumentNullExceptions regardless. Count() is no exception (pardon the pun).
1

If you have your list generation methods return for example List<Ticket> instead of IList<Ticket> it should work out just fine, since List<T> implements IList.

Comments

1

Essentially, the reason your code doesn't work is because the generic IList<T> does not implement the non-generic IList interface (it's only coincidental that List<T> happens to implement both interfaces.).

It appears that the static types of the arguments you are passing are all IList<SomeThing>, so what you have clearly isn't going to work.

One workaround would be to just rely on the classic IEnumerable interface and do all the leg-work in the method itself, i.e. figure out the fastest way to get each sub-sequence's count.

Here's an untested sample:

private static int TotalItemsInLists(params IEnumerable[] lists)
{
    if(lists == null)
       throw new ArgumentNullException("lists");

    return lists.Sum(l => l == null ? 0 : GetCount(l));      
}

private static int GetCount(IEnumerable source)
{
    var collection = source as ICollection;
    if (collection != null)
        return collection.Count;

    var genCollType = source.GetType().GetInterfaces()
                      .FirstOrDefault
                      (i => i.IsGenericType 
                       && i.GetGenericTypeDefinition() == typeof(ICollection<>));

    if (genCollType != null)
        return (int)genCollType.GetProperty("Count").GetValue(source, null);

    return source.Cast<object>().Count();
}

2 Comments

Why do not do return lists.Sum(x=>x.Count()) in your first function?
@Saeed: That wouldn't work; Count is defined for IEnumerable<T>. Remember, we are talking about sequences of different types here. It is a bit of a pity there's no Count() extension for IEnumerable though.
1

If your IList<T>s are all objects that implement ICollection (which is likely, as most of the collection classes in the BCL that implement IList<T> also implement ICollection), then you can simply cast to ICollection, although you will lose type-safety.

private int TotalItemsInLists(params object[] lists)
{
    int total = 0;

    // this casts each object implicitly to ICollection
    foreach (ICollection list in lists)
        if (list != null)
            total += list.Count;

    return total;
}

If not, then you need to go back to the non-generic IEnumerable and use Cast<object>:

private int TotalItemsInLists(params IEnumerable[] lists)
{
    int total = 0;

    foreach (IEnumerable list in lists)
        if (list != null)
            total += list.Cast<object>().Count();

    return total;
}

1 Comment

missing '()' after Cast<object> should be list.Cast<object>().Count();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.