Harold
Harold

Reputation: 1166

Making a Function to Count Elements In Multiple Arrays

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();
    }

Upvotes: 1

Views: 122

Answers (5)

thecoop
thecoop

Reputation: 46108

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;
}

Upvotes: 1

Ani
Ani

Reputation: 113412

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();
}

Upvotes: 1

&#216;yvind Br&#229;then
&#216;yvind Br&#229;then

Reputation: 60694

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.

Upvotes: 1

Quick Joe Smith
Quick Joe Smith

Reputation: 8222

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.

Upvotes: 1

Stefan
Stefan

Reputation: 14880

You have to cast the input to IList:

TotalItemsInLists((IList)jobService.BeingDevelopedBy(person), ...

Upvotes: 1

Related Questions