1

I am writing a method that is passed an Object of any type.

If it is passed a string, it will process this string. If it is passed a class it will process any strings within this class. If this class also had classes, it will process any strings within those classes, and so on.

The code to do this is working really well. The only thing that isn't working is when a collection of class is passed to the method and I need to build some code to handle that.

I've created a non-working sample below that shows what I'm trying to do. In this sample I'm passing this into a generic. It works exactly the same if I swap out the T with an old fashioned Object.

    private static void DoStuff<T>(T ObjectToUse)
    {
        if (ObjectToUse == null || !ObjectToUse.GetType().IsClass) return;

            if (ObjectToUse.Count() > 0) //doesn't work
            {
                foreach (T i in ObjectToUser)
                {
                    ProcessStuff(i);
                }
            }
            else
            {
                ProcessStuff(ObjectToUse);
            }

    }

The problem I have is that T nor Object have a Count. If I use something like the below it returns false, which makes sense as ObjectToUse would be T, not a collection of itself.

if (ObjectToUse is ICollection<T>)

ObjectToUse.GetType() says the type is System.Collections.Generic.HashSet`1['classname']

I'd like to identify that its some form of array, and then cast this into something that I could loop through.

I checked the below, but receives an error saying that is ICollection requires one argument. Determine if object derives from collection type

Resolution:

    private static void DoStuff(object ObjectToUse)
    {
        if (ObjectToUse == null || !ObjectToUse.GetType().IsClass) return;

        if (ObjectToUse is IEnumerable<object>)
        {
            foreach (var obj in (IEnumerable<object>)ObjectToUse)
            {
                ProcessStuff(obj);
            }
        }
        else
        {
            ProcessStuff(ObjectToUse);
        }
    }
Jay
  • 878
  • 3
  • 12
  • 22
  • 1
    "simply didnt work" is not a problem we can easily help with. Can you clarify _what_ didnt work, and why – Jamiec May 18 '21 at 12:32
  • IMHO separate this, you can use constraints, dont stuff everything into one routine. – Trevor May 18 '21 at 12:33
  • 1
    `ObjectToUse` is *of type* `T` but when you iterate through it, you assume it is a *collection* of `T`s (as you use `foreach(T i in ObjectToUse)`) – Rafalon May 18 '21 at 12:35
  • 1
    `foreach` will work correctly and do nothing on an empty collection, so there is no need to check for a `Count` at all. You do not (easily) have access to the element type of the collection, but you don't need it if `ProcessStuff` doesn't, since `foreach` won't care. `if (ObjectToUse is IEnumerable collection) { foreach (object o in collection) { ProcessStuff(o); } }` would do. – Jeroen Mostert May 18 '21 at 12:37
  • Does this answer your question? [Determine if collection is of type IEnumerable](https://stackoverflow.com/questions/1846671/determine-if-collection-is-of-type-ienumerablet) – Trevor May 18 '21 at 12:37
  • @JeroenMostert OP doesn't want to do nothing on an empty collection though... They want to `ProcessStuff(ObjectToUse);`, whatever that does. – Sweeper May 18 '21 at 12:38
  • @Sweeper: As written, yes, but I'd sort of assumed that was a misguided attempt at detecting collectionhood. If separate behavior on an empty collection is truly desired it would still be easy to do without requiring a `Count` by checking if the `foreach` did anything. – Jeroen Mostert May 18 '21 at 12:40
  • @JeroenMostert Oh I see what you mean now! Gosh I read code too literally... – Sweeper May 18 '21 at 12:42
  • What is the end goal of all this? It might be a [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). I.e. this sounds like a bad idea, but it is difficult recommend something else without knowing your actual problem. – JonasH May 18 '21 at 12:45
  • I think Rafalon is on the right track-- ICollection is not of type T. You won't generally have T as a collection of T, that would be some kind of endless recursion. – Christopher Hamkins May 18 '21 at 12:57
  • Thanks. I've improved the description and just testing out the link. Whoever downvoted it, fine, but please say why so I can amend if need be. – Jay May 18 '21 at 13:00
  • Zaggler. Thanks, I got the suggestion in the link working, however unless I'm missing something the suggestion by cph below works with less overhead. – Jay May 18 '21 at 14:32

2 Answers2

1

If your ObjectToUse is of type T, then it will never be of type ICollection<T>; it might be a collection of some other type of object, but not of type T.

You should be able to check if the enumerator is for some other type by writing

if (ObjectToUse is IEnumerable)

since an IEnumerable<T> will also implement

IEnumerator IEnumerable.GetEnumerator()
Christopher Hamkins
  • 1,442
  • 9
  • 18
  • if (ObjectToUse is IEnumerable) required an argument but if (ObjectToSanitise is IEnumerable does work. – Jay May 18 '21 at 13:29
  • I guess you mean if (ObjectTo**Use** is IEnumerable). Sounds like a good solution. – Christopher Hamkins May 18 '21 at 13:37
  • It is legal to declare `class MyCollection : ICollection`, and then T will actually be `ICollection`. I would guess it is not what the author intended however. – JonasH May 18 '21 at 14:51
0

Every collection type, including arrays and IEnumerable, implements IEnumerable

if(typeof(System.Collections.IEnumerable).IsAssignableFrom(typeof(T)))
{
// then you know its a collection and you can iterate it
}
Charles
  • 67
  • 8