By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,188 Members | 844 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,188 IT Pros & Developers. It's quick & easy.

How do I cast an object to an ArrayList or IEnumerable?

P: 2
This is a C# VS 2008 question...

Our system has 2 base classes, SingleEntity and NewPluralEntity. SingleEntity provides access to properties and methods related to manipulating data in a database table and NewPluralEntity is a generic base class that I inherit from to create strongly typed collections of single entities.

Here is the declaration of our NewPluralEntity class:

Expand|Select|Wrap|Line Numbers
  1. public abstract class NewPluralEntity<T> : ICollection<T>, IEnumerable<T>
Here is a sample declaration of one of our inherited plural classes:

Expand|Select|Wrap|Line Numbers
  1. public class Workplaces : NewPluralEntity<Workplace>
My problem is that I have to use reflection to return an ArrayList (or some other type of collection that I may enumerate) that contains my Workplaces and I cannot find any way to get the object that is returned from reflection (PropertyInfo.GetValue()) to cast to an ArrayList (or other suitable type).

For example, I have an Insured object. The Insured object has a Workplaces property. I need to use reflection to return an ArrayList that contains my Workplaces.

Here is the code for my "GetNewPluralEntityPropertyUsingReflection" method:

Expand|Select|Wrap|Line Numbers
  1. public ArrayList GetNewPluralEntityPropertyUsingReflection(string p_propertyName)
  2. {
  3.   PropertyInfo myProperty = this.GetType().GetProperty(p_propertyName);
  4.   object res = myProperty.GetValue(this, null); //this is the fully loaded Insured object in this case
  5.   return (ArrayList)res; //blows up...
  6. }
When I invoke Insured.GetNewPluralEntityPropertyUsingReflection( "Workplaces"); and step into the code and place a breakpoint on my "return (ArrayList)res;" statement I can see that the "res" variable has a value and that its base type is NewPluralEntity<Workplace>, with a count and several other properties that are in NewPluralEntity, but I cannot get the cast to ArrayList to succeed.

Originally, I wanted the GetNewPluralEntityPropertyUsingReflection() method to return a NewPluralEntity<Workplace> but I found no way to cast an object to a generic type when I did not know the type at compile time (in my case the type is just a string "Workplaces"). If someone knows how to do return NewPluralEntity<Workplace> in this case that would be great too but ArrayList will do.

Thanks in advance for any ideas.
Nov 18 '08 #1
Share this Question
Share on Google+
11 Replies


Plater
Expert 5K+
P: 7,872
I am pretty sure you cannot just "sideways" cast one derrived class to a different derrived class, even if they have the same parent object(s)

Is there a reason you need an arraylist so bad? Your object appears to support ICollection<T> and IEnumerable<T> already, just index it like normal?
Nov 18 '08 #2

balabaster
Expert 100+
P: 797
You could reflect over the interfaces the object implements and just cast it...

Expand|Select|Wrap|Line Numbers
  1. Dim t As Type = ReceivedObject.GetType()
  2. If t.GetInterfaces().Contains(IEnumerable) Then
  3.   Return DirectCast(ReceivedObject, IEnumerable)
  4. ElseIf t.GetInterfaces().Contains(IList) Then
  5.   Return DirectCast(ReceivedObject, IList)
  6. ...
Alas, what I think you're trying to achieve (if I interpret correctly) is this:

Return DirectCast(ReceivedObject, ReceivedObject.GetType)

and I've come across numerous times where I've needed to be able to do that, and I've yet to come across a solution that doesn't require me to run a switch to check the type and then cast accordingly...

If you do manage to come up with a better solution, I'd welcome the feedback as I'd love to know there's a better way of doing this...
Nov 18 '08 #3

Expert 100+
P: 190
There might be a more type safe way to do this, but before I work up the code sample, let me get some clarification.
I understand (correct me if wrong)
- You have a generic NewPluralEntity collection.
- You have classes which will have a property which is a strongly typed NewPluralEntity collection, such as a Insured class which has a Workplaces property which is a NewPluralEntity<Workplace>.
- For any of those classes, you want to retrieve a working collection, be it the typed NewPluralEntity<T> collection or an ArrayList of T objects.

First, I don't think you need to go via ArrayList (and yes, that cast should just blow up - generics have limited variance, see Variance in Generic Types ), so lets focus back on getting a strongly typed list. I understand that because it is a generic collection, you don't necessarily know the type at runtime...but...

You state you have a "GetPropertyUsingReflection" method which takes the property name - so you do know SOMETHING at runtime.

My question is: is there a one-to-one relation between your property names and the type?
For example, in this instance, you are retrieving the "Workplaces" property, and expect NewPluralEntity<Workplace>. So will another class have a "Bars" property that is NewPluralEntity<Bar> and another class have a "Foos" property which is NewPluralEntity<Foo>?

If yes, I am pretty sure you can do this by making "GetPropertyUsingReflection(string)" into simply GetNewPluralCollection<T>().

But tell me know before I start if your design intent is to have only one property name ("Workplaces") with no guarantee of the type: i.e. Workplaces can be NewPluralEntity<Workplace> OR NewPluralEntity<Bar>.

There may also be a workaround by modifying how you create the class which contains the collection...but one workthrough at a time...
Nov 19 '08 #4

Expert 100+
P: 190
P.S. balabaster:

"DirectCast" seems to be VB specific and I am not familiar with it. Would that be the same as the C# keyword "as" ?
Expand|Select|Wrap|Line Numbers
  1. return ReceivedObject as IEnumerable;
or is it a cast subject to exception:
Expand|Select|Wrap|Line Numbers
  1. return (IEnumerable)ReceivedObject;
Nov 19 '08 #5

balabaster
Expert 100+
P: 797
P.S. balabaster:

"DirectCast" seems to be VB specific and I am not familiar with it. Would that be the same as the C# keyword "as" ?
Expand|Select|Wrap|Line Numbers
  1. return ReceivedObject as IEnumerable;
or is it a cast subject to exception:
Expand|Select|Wrap|Line Numbers
  1. return (IEnumerable)ReceivedObject;
Yeah, in C# - I think (although don't quote me, I'm not 100% sure without digging) that

CType(MyObj, Type) is equivalent to (Type)MyObj

and...

DirectCast(MyObj, Type) is equivalent to MyObj As Type

I'll confirm and get back to you

Edit[1]: Using the VB to C# converter at developer fusion translates both to (Type)MyObj so maybe I'm incorrect...

Edit[2]: The As keyword in C# converts like so:
Expand|Select|Wrap|Line Numbers
  1. string s = mystring as string;
converts to:
Expand|Select|Wrap|Line Numbers
  1. Dim s As String = TryCast(mystring, string)
(I never came across TryCast before: Here's an article about it - http://www.panopticoncentral.net/arc...03/02/281.aspx)

Expand|Select|Wrap|Line Numbers
  1. Dim s As String = DirectCast(MyObj, String)
is the same as:
Expand|Select|Wrap|Line Numbers
  1. string s = (string)MyObj;
So I'm now trying to figure out what the C# equivalent of CType is... or if this is implicitly handled in C# without the need to differentiate between DirectCast/CType.

Anyway, that's for another thread/article...
Nov 19 '08 #6

Expert 100+
P: 190
I guess I care about this more than the person requesting help, but I wanted to complete the proposed solution. Note: the following is neither the only solution nor the most efficient (in terms of design), but it is works within the constraints of the original question:

Unless someone is interested enough to ask any questions, I will post the following solution as is with the short comments in the code. The objective is to be able to retrieve a strongly typed collection, stored as a property in a class, without knowing anything about the types in the collection at runtime.
However, we know the "name" of the collection property, and this should have a relation to the type of the collection items;
We know we want the items to have some database functionality.

Expand|Select|Wrap|Line Numbers
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5.  
  6. namespace bytes{
  7.  
  8.   // Your collection, as Collection for simplicity's sake.
  9.   public abstract class NewPluralEntity<T> : Collection<T> { }
  10.  
  11.   // Since you want your SingleEntities to have data access, show that they do.
  12.   public interface DBActive { void WriteToDB();}
  13.  
  14.   // Encapsulate your 'GetPropertyByName' method as a function of your class
  15.   public abstract class ManagedGroup {
  16.     public abstract NewPluralEntity<TPlace> GetListOfPlaces<TPlace>();
  17.     public abstract ICollection<DBActive> GetListOfDBItems();
  18.   }
  19.  
  20.   // Example SingleEntity
  21.   public class Workplace : DBActive {
  22.     string s;
  23.     public Workplace(string name) { this.s = name; }
  24.     public string Company { get { return this.s; } }
  25.     public void WriteToDB() { Console.WriteLine("Wrote Workplace {0} to DB.", s); }
  26.   }
  27.  
  28.   // Example SingleEntity
  29.   public class Funplace : DBActive {
  30.     string s;
  31.     public Funplace(string name) { this.s = name; }
  32.     public string Name { get { return this.s; } }
  33.     public void WriteToDB() { Console.WriteLine("Wrote Funplace {0} to DB.", s); }
  34.   }
  35.  
  36.   // Your defined Plural Entity
  37.   public class Workplaces : NewPluralEntity<Workplace> { }
  38.  
  39.   // Another defined Plural Entity
  40.   public class Funplaces : NewPluralEntity<Funplace> { }
  41.  
  42.   // The class containing your collection of Workplaces
  43.   public class Insured : ManagedGroup {
  44.     Workplaces internalList;
  45.     public Insured() {this.internalList = new Workplaces();}
  46.     public Workplaces PlacesToWork { get { return this.internalList; } }
  47.     public override NewPluralEntity<TPlace> GetListOfPlaces<TPlace>() {
  48.       if (typeof(TPlace) == typeof(Workplace))
  49.         return this.internalList as NewPluralEntity<TPlace>;
  50.       else
  51.         throw new InvalidCastException();
  52.     }
  53.     public override ICollection<DBActive> GetListOfDBItems() {
  54.       List <DBActive> tempList = new List<DBActive>(this.internalList.Count);
  55.       foreach (Workplace w in this.internalList) tempList.Add(w);
  56.       return tempList;
  57.     }
  58.   }
  59.  
  60.   // Another class containing a collection of Funplaces
  61.   public class AtRisk : ManagedGroup {
  62.     Funplaces internalList;
  63.     public AtRisk() { this.internalList = new Funplaces(); }
  64.     public Funplaces PlacesToPlay { get { return this.internalList; } }
  65.     public override NewPluralEntity<TPlace> GetListOfPlaces<TPlace>() {
  66.       if (typeof(TPlace) == typeof(Funplace))
  67.         return this.internalList as NewPluralEntity<TPlace>;
  68.       else
  69.         throw new InvalidCastException();
  70.     }
  71.     public override ICollection<DBActive> GetListOfDBItems() {
  72.       List<DBActive> tempList = new List<DBActive>(this.internalList.Count);
  73.       foreach (Funplace f in this.internalList) tempList.Add(f);
  74.       return tempList;
  75.     }
  76.   }
  77.  
  78.   public class Program{
  79.     static void Main() {
  80.  
  81.       // SOMEWHERE (anywhere) you will have to create the Insured class and, you will know what it is.
  82.       Insured insuredGroup = new Insured();
  83.       insuredGroup.PlacesToWork.Add(new Workplace("GM"));
  84.       insuredGroup.PlacesToWork.Add(new Workplace("Chrysler"));
  85.       insuredGroup.PlacesToWork.Add(new Workplace("Ford"));
  86.  
  87.       // SOMEWHERE (anywhere) you will have to create the AtRisk class and, you will know what it is.
  88.       AtRisk atRiskGroup = new AtRisk();
  89.       atRiskGroup.PlacesToPlay.Add(new Funplace("Disneyland"));
  90.       atRiskGroup.PlacesToPlay.Add(new Funplace("Six Flags"));
  91.       atRiskGroup.PlacesToPlay.Add(new Funplace("Sears Racepoint"));
  92.  
  93.       // This assummes that if you know you are looking for the property name "Workplaces"
  94.       // you can take advantage and get the strongly typed collection.
  95.       NewPluralEntity<Workplace> insuredPlaces = insuredGroup.GetListOfPlaces<Workplace>();
  96.       NewPluralEntity<Funplace> funPlaces = atRiskGroup.GetListOfPlaces<Funplace>();
  97.  
  98.       foreach (Workplace w in insuredPlaces) Console.WriteLine(w.Company);
  99.       foreach (Funplace f in funPlaces) Console.WriteLine(f.Name);
  100.       Console.WriteLine();
  101.  
  102.       // If you have NO IDEA what object you are dealing with, you can still have it execute 
  103.       // the desired Database action.
  104.       ManagedGroup managedGroupA = insuredGroup as ManagedGroup;
  105.       ManagedGroup managedGroupB = atRiskGroup as ManagedGroup;
  106.       ICollection<DBActive> listDBItemsA = managedGroupA.GetListOfDBItems();
  107.       ICollection<DBActive> listDBItemsB = managedGroupB.GetListOfDBItems();
  108.       foreach (DBActive dbItem in listDBItemsA) dbItem.WriteToDB();
  109.       foreach (DBActive dbItem in listDBItemsB) dbItem.WriteToDB();
  110.  
  111.     }
  112. }
  113.  
Output:
Expand|Select|Wrap|Line Numbers
  1. GM
  2. Chrysler
  3. Ford
  4.  
  5. Disneyland
  6. Six Flags
  7. Sears Racepoint
  8.  
  9. Wrote Workplace GM to DB.
  10. Wrote Workplace Chrysler to DB.
  11. Wrote Workplace Ford to DB.
  12.  
  13. Wrote Funplace Disneyland to DB.
  14. Wrote Funplace Six Flags to DB.
  15. Wrote Funplace Sears Racepoint to DB.
  16.  
Nov 20 '08 #7

balabaster
Expert 100+
P: 797
Damn that's a lot of reading... :oP
Nov 20 '08 #8

Plater
Expert 5K+
P: 7,872
without the need to differentiate between DirectCast/CType.
The only difference between those two is one allows casting to "similar" or base types (can cast a double to an int, with loss, for example) and the other requires an exact type.
(type)object works like the one that allows casting to similar or base types.

For the functionality of the other, I think you would just go
if(object.GetType() == (type))
Nov 20 '08 #9

P: 2
Thanks to everyone for your thoughts. I did not get any notifications about all of the activity and I apologize for not keeping current.

Anyhow, here is how I finally solved my problem. I'll read and fully process the other thoughts as well but since I was able to think of a solution I figured I better post it as an alternative.

Basically, I decided, "If reflection is what is causing my problem maybe I can use reflection to solve my problem." So I decided to use the Invoke() method of the MethodInfo class to invoke my ToArrayList() method on my NewPluralEntity<T> base class.

I am not sure if this is the fastest code in the world but it does work and it allows me to return the ArrayList even though the type of my generic if unknown.

Expand|Select|Wrap|Line Numbers
  1.     public ArrayList GetPluralEntityPropertyUsingReflection(string p_propertyName)
  2.     {
  3.       System.Reflection.PropertyInfo myProperty = this.GetType().GetProperty(p_propertyName);
  4.       object res = myProperty.GetValue(this, null); 
  5.       Type t = res.GetType();
  6.       System.Reflection.MethodInfo mi = t.GetMethod("ToArrayList");
  7.       return (ArrayList)mi.Invoke(res, null); 
  8.     }
  9.  
Thanks for all of the replies. Next time I won't rely on being subscribed to threads and check them every day.
Dec 11 '08 #10

Curtis Rutland
Expert 2.5K+
P: 3,256
@Plater
I found the explanation of the difference on the MSDN:

The as operator is like a cast except that it yields null on conversion failure instead of raising an exception.
Link: as (C#)
Dec 12 '08 #11

balabaster
Expert 100+
P: 797
Good find - thanks for the update.
Dec 12 '08 #12

Post your reply

Sign in to post your reply or Sign up for a free account.