Solved

Doing Foreach construct on unknown types at runtime

Posted on 2007-03-21
26
620 Views
Last Modified: 2010-04-16
I have a method which has a parameter of type IList<myType>.

I would like the method to determine what type is 'myType' (which I can already do),
then iterate over the list using a foreach construct. The problem is I cannot declare
the foreach(myType t in IList) because I don't have an object of the unknown type
myType passed in. If we knew that the type of 'myType' was type 'FooBar' then I could
write the following:
foreach(Foobar fb in myIList)
{
...do stuff
}

How can I get a the named type and use it as shown above?

thx,
ipaman
0
Comment
Question by:ipaman
  • 9
  • 7
  • 6
  • +1
26 Comments
 
LVL 25

Expert Comment

by:dstanley9
Comment Utility
Maybe I'm missing something, but this is how you use generic lists:

public void MyFunction(IList<myType> myIList)
{
  foreach(myType item in myIList)
  {
    // do something with the item
  }
}

are you trying to "cast" the item to another type that is related to myType?
0
 

Author Comment

by:ipaman
Comment Utility
The problem is that 'myType is unknown at runtime.

'myType' is actually an interface that all objects implement. I want to be able to
figure out the type of 'myType' (which I can already do using .GetType()) and
then iterate over the list using the "Actual" type.

e.g.
main()
{
    IList<interfaceType> myList = new .....;
    myList.add_items_to_list();
    MyClass.DoMethod(myList);
}

class MyClass
{
  ...
  public void DoMethod(IList<interfaceType> myList)
  {
     Type  listType = myList[0].GetType();

     foreach (??? x in myList)
      {
         ...do stuff
      }
  }
}

Actual type would replace the ??? here.
0
 
LVL 25

Assisted Solution

by:dstanley9
dstanley9 earned 62 total points
Comment Utility
You won't be able to do that because the foreach needs to know the type at compile time.  You'd have to use reflection to do anything other than what the interface provides, or explicitly try to cast to different types.  

How are you going to "do stuff" to an unknown type?  what kinds of "stuff" are you going to do?  could it be wrapped into the common interface?
0
 

Author Comment

by:ipaman
Comment Utility
So you are saying that there is no way to create an object via Activator.CreateInstance(..)
(or somthing) to use in the Foreach construct??
I could cast ut I would need a whopping switch or if else conditional to manage which
type I need to cast to.

In the end I will be accessing the properties (getter/setter) (using reflection) of the class type in the
IList.
0
 
LVL 13

Expert Comment

by:Webstorm
Comment Utility
Hi ipaman,

If you can use interfaceType in your loop :
  public void DoMethod(IList<interfaceType> myList)
  {
     Type  listType = myList[0].GetType();

     foreach (interfaceType x in myList)
      {
         ...do stuff
      }
  }

In the loop, if you want to use specific functionnality of a specific class :
    if (x is ...type... )
           (x as ...type...).member ...
0
 

Author Comment

by:ipaman
Comment Utility
WebStorm,

the foreach statement would have to have the type of object that implements the
interfaceType interface. The interface itself does not have the members I would be
interrogating. It is the classes that implement this interface that I need to "foreach" through.
0
 

Author Comment

by:ipaman
Comment Utility
WebStorm,
inside the loop would be a gigantic if-else conditional to handle all the type that implement
the interface.
ipaman
0
 
LVL 25

Expert Comment

by:dstanley9
Comment Utility
But how are you going to know what properties to access if you don't know what type the object is?  
0
 
LVL 37

Accepted Solution

by:
gregoryyoung earned 63 total points
Comment Utility
I am confused ...

"In the end I will be accessing the properties (getter/setter) (using reflection) of the class type in the
IList."

why do you need to iterate as the type then? :) you only need to cast to the type if you wanted to access something from that type ...

foreach(object o in MyList) {
   //do stuff with reflections ...
}

But if you are doing this why even use IList<T> when you can just use the non-generic IList and remove all confusion.

public void Iterate(IList List) {
    foreach(object o in List) {
        //do your stuff
    }
}

List<FooBar> list = new List<FooBar>();
list.Add(new FooBar());
Iterate(list);

the key being since you dont actually need anything off the conversion you shouldnt even be botherring to do it.

Cheers,

Greg
0
 
LVL 13

Expert Comment

by:Webstorm
Comment Utility

If you want to access member variable, you should use methods you declare in the interface. So you can use the interface.
Otherwise you'll have to use multiple if tests.

Another way is to make this method a static method in each class :

  public static void DoMethod(IList<class> myList)
  {
     foreach (class x in myList)
      {
         ...do stuff
      }
  }
0
 

Author Comment

by:ipaman
Comment Utility
dstanley9, exactly. I am trying to prevent using a big honkin' if-else conditional by doing something slick
like getting the type and creating it for use in the foreach constuct. From what I'm hearing, that is not possible.

gregoryyoung, I have to iterate as the class type because the interface does not define these...(unless I am confused about classes implementing interfaces. the implementating class takes on the type of the interface....correct?)... In your 'foreach' above the object o only sees the methods of the interface. I need to be able to see the methods of the impementing class so I can call the methods in the foreach (for any IList<interfaceType> type).


WebStorm, The interface wasn't designed to contain accessor methods. The accessor methods for
each subtype are different.
0
 
LVL 37

Expert Comment

by:gregoryyoung
Comment Utility
"gregoryyoung, I have to iterate as the class type because the interface does not define these...(unless I am confused about classes implementing interfaces. the implementating class takes on the type of the interface....correct?)... In your 'foreach' above the object o only sees the methods of the interface. I need to be able to see the methods of the impementing class so I can call the methods in the foreach (for any IList<interfaceType> type)"

your understanding of interfaces and types is wrong .. it will always be the same type when viewed through reflections ..

You said above you were only using reflections on the unknown type.

"In the end I will be accessing the properties (getter/setter) (using reflection) of the class type in the
IList."

I am confused now exactly what you are trying to do here. If you are using reflections you dont need to treat it as a certain type ... you would only need to do that if you wanted to access something in a typed way/
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 25

Expert Comment

by:dstanley9
Comment Utility
" getting the type and creating it for use in the foreach constuct. From what I'm hearing, that is not possible."

Well, technically it's possible using reflection, but you have to know what the type is in order to know what properties to access.  For example, suppose you have the following:

public interface ICommonMethods{
  void CommonMethod1();
  void CommonMethod2();
}

public class Impl1 : ICommonMethods{
  public void CommonMethod1() { return "foo"; }
  public void CommonMethod2() { return "bar"; }
  public string CustomProp1  {
    get    {
       return "CustomProp1";
    }
  }
}

public class Impl2 : ICommonMethods{
  public void CommonMethod1() { return "foo"; }
  public void CommonMethod2() { return "bar"; }
  public string CustomProp2  {
    get {
       return "CustomProp2";
    }
  }
}

Now _technically_ you can use reflection to call the properties CustomProp1 and CustomProp2 on each class, but if you don't know the type at design time, how are you going to know which properties to try and access?  You'd have to use a switch or if staments to see if the property exists on that class.

Now, if you want to use that interface as-is, but your implementations share common properties, you can either use a separate interface and have the classes implement both, ot you can define a new interface that inherits from the original:

public interface ICustomProperties: ICommonMethods  {
  string CustomProp1 {get};
  string CustmoProp2 {get};

}

Then your types would inherit from ICustomProperties and your list code would be:

  public void DoMethod(IList<ICustomProperties> myList)  {
     foreach (ICustomProperties x in myList)  {
         string s1 = x.CustomProp1;
         string s2 = x.CustomProp2;
         x.CommonMethod1();
         x.CommonMethod1();
      }
  }

0
 

Author Comment

by:ipaman
Comment Utility
Take a look at my current pseudo code. Hopefully this will explain better than I have been.

interface IEntity{
long Id { get; set; }
}

public class foo : IEntity{
  public Int64 Id {
    get{ return id; }
    set{ id = value;}
  }
  public string Prop1{
     get{ return prop1; }
     set{ prop1 = value;}
  }
}
public class bar : IEntity{
  public Int64 Id {
    get{ return id; }
    set{ id = value;}
  }
  public string Prop2{
     get{ return prop2; }
     set{ prop2 = value;}
  }
}

public class driver{
  private IList<foo> list1 = new List<foo>();
  private IList<foo> list2 = new List<foo>();
  private IList<bar> list3 = new List<bar>();
  private IList<bar> list4 = new List<bar>();

  public driver()
  {
    BuildLists();
    // !!!!!!!! I could have passed in list3 and list 4 here instead.
    //.....that's why my ListComparer class doesn't know the type at runtime. !!!!!!!!!!
     ListComparer.Compare(list1, list2);
  }
  public void BuildLists()
  {
    //Let's pretend we set Prop1 on foo here before we added to the list.......
     list1.Add(new foo());
     list1.Add(new foo());
     list2.Add(new foo());
     list2.Add(new foo());
   
 //Let's pretend we set Prop2 on bar here before we added to the list.......
     list3.Add(new bar());
     list3.Add(new bar());
     list4.Add(new bar());
     list4.Add(new bar());
  }
}

public class ListComparer
{
   public static int Compare(IList<IEntity> x, IList<IEntity> y)
   {
      Type t = x[0].GetType(); //now I have the type of the IEntity in this case "foo"
     
      // Now I cannot do a foreach on this list because I don't have a class type to use in the foreach
      foreach( 'Type goes here' t in x)
      {
      }
   }
}
0
 
LVL 37

Expert Comment

by:gregoryyoung
Comment Utility
There is no code there that NEEDS the type from what you keep saying about using reflections ...

public class foo {
  public Int64 Id {
    get{ return id; }
    set{ id = value;}
  }
  public string Prop1{
     get{ return prop1; }
     set{ prop1 = value;}
  }
}
public class bar {
  public Int64 Id {
    get{ return id; }
    set{ id = value;}
  }
  public string Prop2{
     get{ return prop2; }
     set{ prop2 = value;}
  }
}

public class driver{
  private IList<foo> list1 = new List<foo>();
  private IList<foo> list2 = new List<foo>();
  private IList<bar> list3 = new List<bar>();
  private IList<bar> list4 = new List<bar>();

  public driver()
  {
    BuildLists();
    ListComparer.Compare(list1, list2);
  }
  public void BuildLists()
  {
    //Let's pretend we set Prop1 on foo here before we added to the list.......
     list1.Add(new foo());
     list1.Add(new foo());
     list2.Add(new foo());
     list2.Add(new foo());
   
 //Let's pretend we set Prop2 on bar here before we added to the list.......
     list3.Add(new bar());
     list3.Add(new bar());
     list4.Add(new bar());
     list4.Add(new bar());
  }
}

public class ListComparer
{
   public static int Compare(IList x, IList y)
   {
      // Now I cannot do a foreach on this list because I don't have a class type to use in the foreach
      foreach(object o in x)
      {
      }
   }
}

Of course since you are doing an equality compare here shouldnt you just be using the IComparable etc of the actual items? :) i.e. make each item able to compare itself to another item of its type .. then just iterate through the two lists telling the nodes to compare to each other .. and this whole problem goes away.

0
 
LVL 25

Expert Comment

by:dstanley9
Comment Utility
Actually this won't work at all as you can't cast IList<foo> to IList<IEntity> - even though you can cast foo to IEntity.  
0
 
LVL 25

Expert Comment

by:dstanley9
Comment Utility
But even so... within Compare how do you know whether to check the Prop1 property or Prop2 property?  You can do it using reflection, but you have to explicitly say which properties you check, so you're going to have a big switch or if block anyway?  
0
 
LVL 37

Expert Comment

by:gregoryyoung
Comment Utility
if each object is IComparable then it does the check itself ...

public class foo : IComparable {
  public Int64 Id {
    get{ return id; }
    set{ id = value;}
  }
  public string Prop1{
     get{ return prop1; }
     set{ prop1 = value;}
  }
  public integer CompareTo(object o) {
     foo f = o as foo;
     if(foo == null) throw new ArgumentException("o must be a foo");
     return this.Prop1.CompareTo(f.Prop1);
  }
}
public class bar :IComparable {
  public Int64 Id {
    get{ return id; }
    set{ id = value;}
  }
  public string Prop2{
     get{ return prop2; }
     set{ prop2 = value;}
  }
  public integer CompareTo(object o) {
     bar f = o as bar;
     if(bar == null) throw new ArgumentException("o must be a bar");
     return this.Prop2.CompareTo(f.Prop2);
  }
}


or you could pass in an IComparer to do the comparison ...

Cheers,

Greg
0
 

Author Comment

by:ipaman
Comment Utility
dstanley9,

that's my point exactly. I am trying to do this generically but since I am hearing that it is impossible, I then have to have a gigantic switch (if-else) statement like I mentioned in my second comment.

gregoryyoung,
this question is not about whether or how to use IComparer. It is directed toward passing and using
ILists in a more generic way :-)
0
 
LVL 37

Expert Comment

by:gregoryyoung
Comment Utility
ipaman .. if you are ONLY using reflections you DONT NEED to cast it to a given type.

You are manufacturing a problem.

If you need to do things on the items you can use polymorphism on the items to do it. All of this without typing.

I cant say it enough that you dont need to be typing here and you certainly dont need a case statement.
0
 

Author Comment

by:ipaman
Comment Utility
gregoryyoung,
ok then, if you have code that solves my situation (without modifying any code in the
implementing classes, i.e. class foo and bar) and allows the Compare method of ListComparer
class to take in an IList (of any type) and be able to call the methods of that type (i.e. foo.Prop1)
and not modifiy the Interface class by adding methods.......
then I will be greatful for an answer and will award the points.
thx,
ipaman
0
 
LVL 13

Expert Comment

by:Webstorm
Comment Utility

To pass any generic list, you may use a generic method like this one :

public class ListComparer
{
   public static int Compare<T>(IList<T> x, IList<T> y)
   {
      // object type is T    
      foreach( T t in x )
      {
           // ...
      }
   }
}

But then, you need to know the property name you have to access :
   - change the name in foo & bar classes to have the same (Prop for example)
             t.Prop
   - or test each class in the loop using  is  &  as
   - or add access method in the ListComparer

public class ListComparer
{
      public static string GetProperty(foo o)
      { return o.Prop1; }
      public static string GetProperty(bar o)
      { return o.Prop2; }
}

->    GetProperty(t)
0
 

Author Comment

by:ipaman
Comment Utility
WebStorm,

I cannot change the properties in the classes foo and bar. I wish I could but I do not have the authority
to do so. This wouldn't be a problem for me if the foo and bar classes had the exact
same proerty names:-) I wouldn't need to cast or manufacture any particular class type.

ipaman
0
 
LVL 37

Expert Comment

by:gregoryyoung
Comment Utility
ipaman please the the interface IComparer<T>

public class ListComparer<T>
{
   private Comparer<T> m_Comparer;
   public ListComparer(Comparer<T> _Comparer) {
        m_Comparer = _Comparer;
   }
   public  int Compare(IList<T> x, IList<T>)
   {
      // object type is T    
      foreach( T t in x )
      {
           foreach(T u in y) {
                bool same =  m_Comparer.Compare(t, u);
           }      
      }
   }
}

It will work with any Comparer<T> ... just pass it in the constructor.

0
 
LVL 37

Expert Comment

by:gregoryyoung
Comment Utility
I would like to know why the answer given "does not work"
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Suggested Solutions

This article describes a simple method to resize a control at runtime.  It includes ready-to-use source code and a complete sample demonstration application.  We'll also talk about C# Extension Methods. Introduction In one of my applications…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now