0

I have a List containing different kinds of objects and sometimes those objects have a list of their own. So, to get OfType<> of the list AND children (wherever applicable), I am creating a recursive function. However, I can't seem to use the extension method with List<object>

        public static List<T2> OfTypeRecursive<T1, T2>(this List<T1> l) where T2 : Type
        {
            var retList = l.OfType<T2>().ToList();

            foreach (var v in l)
                if(v.GetType() == typeof(loopLoopClass))
                    retList.AddRange(((loopLoopClass)(object)v).loopBody.OfTypeRecursive<T2>());

            return retList;
        }

I get this error

'List<object>' does not contain a definition for 'OfTypeRecursive' and no extension method 'OfTypeRecursive' accepting a first argument of type List<object> could be found (are you missing a directive or assembly reference?)

however, the extension method works if I change this List<T1> l to this List<object> l. I would really appreciate any help. Thanks

Edit : I am using .net core

Grimson
  • 540
  • 1
  • 5
  • 21
  • Consider using more descriptive variable names. I get that l is a list, but what is `v` supposed to be? I'm also curious why you are casting `v` to `object` then to `loopLoopClass` – Kevin R. Aug 08 '17 at 05:12
  • Also, can you provide a code snippet that provides the error message? – Kevin R. Aug 08 '17 at 05:14
  • typecasting to ```object``` then to ```loopLoopClass``` because I couldn't cast from ```T1``` to ```loopLoopClass``` directly. It game me the error that there is no implicit conversion. Also, the variable names are bad because this is just a sample for testing the concept. I planned to rewrite the function before adding to main codebase. – Grimson Aug 08 '17 at 05:24
  • Also, I am using .net core if it matters – Grimson Aug 08 '17 at 05:24
  • @KevinR. This IS the code snippet. This is a recursive version of ```OfType```. I planned to obtain the list in case a member of the List is of a certain type. However I couldn't call the extension method from inside again. The error I got is mentioned in the original post. – Grimson Aug 08 '17 at 05:30
  • 1
    Can you tell us what is `loopLoopClass`? How do you use the method and what the expected result it? – kuskmen Aug 08 '17 at 05:37
  • @kuskmen it contains details of a loop (named 'loop'). So, it has the conditions. body etc. Since this method "should be" a recursive version of ```OfType```, it will fetch the required objects from the AST. – Grimson Aug 08 '17 at 05:40
  • 1
    @Crimson7 what is the definition of `loopBody` ? `public List loopBody` ? – kuskmen Aug 08 '17 at 05:45
  • @kuskmen ```public List loopLoopBody``` – Grimson Aug 08 '17 at 05:47

4 Answers4

1

I am not sure I understood what you want , but I will give it a try.

First, you can remove second generic parameter and replace it with Type directly, then you need to make extension for the List<object> rather than List<T> and this will guarantee compile-time safety

public static List<Type> OfTypeRecursive<T>(this List<object> list)
{
    var retList = list.OfType<Type>().ToList();

    foreach (var v in list)
        if (v.GetType() == typeof(loopLoopClass))
            retList.AddRange(((loopLoopClass)v).loopBody.OfTypeRecursive<Type>());

    return retList;
}

Since List<object> != List<T> it will not work like you expect it. Covariance and contravariance is topic that might help you understand this.

kuskmen
  • 3,648
  • 4
  • 27
  • 54
  • why would an extension method for `List` not work with `List`? – Grimson Aug 08 '17 at 05:58
  • 1
    Copied and pasted your example , and to me error was that it `cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable` , how do you reproduce your bug? Show us more code. – kuskmen Aug 08 '17 at 06:10
  • It is meant to be called like ` var val = Tree.OfTypeRecursive()` or something close to it where `variableAssignClass` itself is a class holding details of any variable assigns. – Grimson Aug 08 '17 at 08:43
  • Strange, when I go over all the elements of the list outside the extension method, I get `AHKCore.BaseVisitor+variableAssignClass, AHKCore.BaseVisitor+classDeclarationClass, AHKCore.BaseVisitor+variableAssignClass` but when I debug the extension method for `variableAssignClass`, I get nothing at `OfType` line. Also, could you tell me how `OfType` gets it parameter while we are passing no variable to it? – Grimson Aug 08 '17 at 09:04
  • `var retList = list.OfType().ToList();` doesn't work, but `var retList2 = list.OfType();` does? Am I doing something wrong? – Grimson Aug 08 '17 at 09:35
  • I think you are confusing type type with your custom type they are not interchangable – kuskmen Aug 08 '17 at 10:03
  • I think you're right. Is there any way I can make it work though? `var retList = list.OfType().ToList();` doesn't work, but `var retList2 = list.OfType();` does, and I need to call it like `var val = Tree.OfTypeRecursive()` – Grimson Aug 08 '17 at 10:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151372/discussion-between-crimson7-and-kuskmen). – Grimson Aug 08 '17 at 11:12
1

I have a List containing different kinds of objects and sometimes those objects have a list of their own.

This relationship needs to be encoded and it is the one that will drive your type constraint method.

Assuming you have control over your concrete object implementation, the encoding might look like this:

public interface INestedList
{
    List<object> InnerList { get; }
}

public class ContainsNestedList : INestedList
{
    public List<object> InnerList { get; }

    public string Name { get; }

    public ContainsNestedList(string name, List<object> innerList)
    {
        Name = name;
        InnerList = innerList;
    }

    public override string ToString()
    {
        return Name;
    }
}

Note: The Name property of ContainsNestedList is only there for visual identification purposes.

If you don't have control over the concrete object types, there should still be some sort of existing logic that gives you this T1 - T2 relationship, otherwise the premise of your problem doesn't hold up.

When you do foreach (var v in l) in your code, because l is a List<T1>, v is also a T1. Furthermore, checking to see that v's type is the type of loopLoopClass means that T1 is either an object (non-constrained type) or a descendant of the loopLoopClass, the latter being translated to a constraint of where T1 : loopLoopClass.

Given that you do loopBody.OfTypeRecursive<T2>() afterwards, and that loopBody is said to be List<object> according to the comments, T1 is now established to be object.

That would lead us down the following code path:

public static class ListExtensions
{
    public static List<object> OfTypeRecursive<T>(this List<object> source)
        where T : INestedList
    {
        var itemsToDigInto = source.OfType<T>().ToList();
        var output = source.Except(itemsToDigInto.Cast<object>()).ToList();

        foreach (T item in itemsToDigInto)
        {
            Console.WriteLine($"Extracting from InnerList of {item}");

            output.AddRange(item.InnerList.OfTypeRecursive<T>());

            Console.WriteLine($"Finished processing for {item}");
        }

        return output;
    }
}

public class Program
{
    public static void Main()
    {
        var x = 2;
        var y = 4.3m;
        var z = "sf";
        var p = new ContainsNestedList("p", new List<object> { x, y });
        var q = new ContainsNestedList("q", new List<object> { z, p });
        var r = new ContainsNestedList("r", new List<object> { x, y, p, q });

        var source = new List<object> { x, y, z, q, p, r };

        var result = source.OfTypeRecursive<ContainsNestedList>();

        result.ForEach(i => Console.WriteLine("{0}", i));

        Console.ReadKey();
    }
}

EDIT: Update to account for the following premise

you have a list of arbitrary objects. Some of these objects are of type ClassA. Some of the other objects are of some other specific class, say ClassB, which is defined as having an inner list of objects, some of which might also be ClassA or ClassB in type.

public static class ListExtensions
{
    public static List<T> OfTypeRecursive<T, TU>(this List<object> source)
        where TU : INestedList
    {
        var output = source.OfType<T>().ToList();
        var itemsToDigInto = source.OfType<TU>();

        foreach (TU item in itemsToDigInto)
        {
            Console.WriteLine($"Extracting from InnerList of {item}");

            output.AddRange(item.InnerList.OfTypeRecursive<T, TU>());

            Console.WriteLine($"Finished processing for {item}");
        }

        return output;
    }
}
Mihai Pantea
  • 360
  • 3
  • 8
  • That is an elegant solution. but you have premise of the question wrong. I have to get a class (let's say `ClassA`) from the list and from some of the children that have a List of their own. So that would give me all the ClassA's from the source list. Still the solution is pretty close. – Grimson Aug 08 '17 at 08:38
  • So let me get the premise straight: you have a list of arbitrary objects. Some of these objects are of type `ClassA`. Some of the other objects are of some other specific class, say `ClassB`, which is defined as having an inner list of `ClassA`? – Mihai Pantea Aug 08 '17 at 09:15
  • Pretty much. Might be better understood as a function body implemented as a List (original source). We need to get variable Assigns (`classA`) from the function body. These `ClassA`s can be directly in a function, or might be inside a loop or if-else statements (`ClassB`). We need to get ALL of the `ClassA`s, and so I am attempting to do it recursively. – Grimson Aug 08 '17 at 09:24
  • shouldn't `itemsToDigInto` be `source.Except(output.Cast()).ToList();`? – Grimson Aug 08 '17 at 10:24
  • Not if you know that the only remaining objects of interest (after plucking out the `T`s) are the ones that contain an inner list of the `T`s you're interested in, and these objects adhere to the `INestedList` contract – Mihai Pantea Aug 08 '17 at 10:51
0

See the answer on this post.

Here's an example on defining an extension method for a generic List:

using System;
using System.Collections.Generic;

namespace ExtensionMethodTester
{
  public class Program
  {
      public static void Main(string[] args)
      {
          List<string> myList = new List<string>();
          myList.MyTestMethod();
      }
  }

  public static class MyExtensions 
  {
    public static void MyTestMethod<T>(this List<T> list)
    {
         Console.WriteLine("Extension method called");
    }
  }
}
M3talM0nk3y
  • 1,382
  • 14
  • 22
  • I have defined an extension method for a list before, so that's not an issue. However I couldn't call the method recursively using a ```List``` as mentioned in the code – Grimson Aug 08 '17 at 05:26
0

The problem ist your definition of

public List<object> loopLoopBody

Like the error messages tells you, object does not have a method

OfTypeRecursive

So it looks like that you're calling the method on the list item, not on the list. You should consider to use your types T1 and T2 for casting and not "loopLoopClass" and "object". Maybe that solves your problem.

user743414
  • 936
  • 10
  • 23