Problem casting generic object to specific object

Please support our C# advertiser: Intel Parallel Studio Home
Reply

Join Date: Oct 2009
Posts: 2
Reputation: dhaval_shah is an unknown quantity at this point 
Solved Threads: 0
dhaval_shah dhaval_shah is offline Offline
Newbie Poster

Problem casting generic object to specific object

 
0
  #1
Oct 8th, 2009
I've created the following generic method
  1. protected int GetSpecifiedIndexByName<T>(T collection, string itemName)
  2. {
  3. int count = 0;
  4. foreach (MyElement claim in collection)
  5. {
  6. if (itemName.Equals(claim.Name))
  7. {
  8. return count;
  9. }
  10. count++;
  11. }
  12. }

Which is called by

  1. int index = GetSpecifiedIndexByName<MyCollection>(claimColl, itemName);

However, when I compile this I get the error

Error 1 foreach statement cannot operate on variables of type 'T' because 'T' does not contain a public definition for 'GetEnumerator'

I cannot cast the generic collection to the known collection directly i.e.

  1. MyCollection foo = (MyCollection)collection;

But have to take the intermediary step

  1. Object foo = collection;
  2. MyCollection foobar = (MyCollection)foo;

This is clearly a lack of understanding about generics (and my 1st attempt at it) but any answers relating to why I get the problems (and solution) would be much appreciated.

Thanks

Dhaval
Reply With Quote Quick reply to this message  
Join Date: Feb 2009
Posts: 3,346
Reputation: sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of sknake has much to be proud of 
Solved Threads: 603
Sponsor
sknake's Avatar
sknake sknake is offline Offline
.NET Enthusiast
 
0
  #2
Oct 8th, 2009
Upload a sample project demonstrating this behavior. You can upload a project by clicking on "Go Advanced" and then "Manager Attachments". You need to implement IEnumerable to use foreach() .
Scott Knake
Custom Software Development
Apex Software, Inc.
Reply With Quote Quick reply to this message  
Join Date: Jun 2005
Posts: 2,052
Reputation: Rashakil Fol is just really nice Rashakil Fol is just really nice Rashakil Fol is just really nice Rashakil Fol is just really nice 
Solved Threads: 139
Team Colleague
Rashakil Fol's Avatar
Rashakil Fol Rashakil Fol is offline Offline
Super Senior Demiposter
 
4
  #3
Oct 8th, 2009
Originally Posted by dhaval_shah View Post
I've created the following generic method
  1. protected int GetSpecifiedIndexByName<T>(T collection, string itemName)
  2. {
  3. int count = 0;
  4. foreach (MyElement claim in collection)
  5. {
  6. if (itemName.Equals(claim.Name))
  7. {
  8. return count;
  9. }
  10. count++;
  11. }
  12. }
You have two problems with this method. One is that there is no proof that T : IEnumerable. You need to use a where clause to specify that. The other problem is that not all control paths lead to a return statement.

You shouldn't be using generics for this kind of behavior. Instead, you should just have the function take an object that implements the IEnumerable interface:
  1. protected int GetSpecifiedIndexByName(IEnumerable collection, string itemName)
  2. {
  3. int count = 0;
  4. foreach (MyElement claim in collection)
  5. {
  6. if (itemName.Equals(claim.Name))
  7. {
  8. return count;
  9. }
  10. count++;
  11. }
  12. return -1;
  13. }
You could also use IEnumerable<MyElement> in the signature. This is a good idea because it more tightly defines the interface of the function.
  1. protected int GetSpecifiedIndexByName(IEnumerable<MyElement> collection, string itemName)
  2. {
  3. ...
  4. }

So, you should not be trying to implement a generic function at all. You just want a function that takes a parameter of type IEnumerable<MyElement>.

I cannot cast the generic collection to the known collection directly i.e.
In general, you can't cast from one type to an unrelated type. For example, you can't cast something from Int32 to ArrayList. This is because there's no way that can be correct! Similarly, you can't cast a generic type, which could be anything, to the type MyCollection. You can only cast things to superclasses and subclasses of the type you're casting.

But have to take the intermediary step

  1. Object foo = collection;
  2. MyCollection foobar = (MyCollection)foo;
In this example, you've cast up to Object (which is allowed because Object is a superclass of everything) and then you've cast down to MyCollection (which is allowed because MyCollection is a subclass of Object). The fact that you wish to do such a thing means you're trying to use the type system the wrong way, and I've already described how.

By the way, here's another possible way to write the function you want. It uses the where clause that I mentioned at the beginning of my reply. This would be a spurious use of generics, though, since generics are only useful when you want to prove that two types are equal.

  1. protected int GetSpecifiedIndexByName<T>(T collection, string itemName)
  2. where T : IEnumerable<MyElement>
  3. {
  4. int count = 0;
  5. foreach (MyElement claim in collection)
  6. {
  7. if (itemName.Equals(claim.Name))
  8. {
  9. return count;
  10. }
  11. count++;
  12. }
  13. return -1;
  14. }
All my posts may be redistributed under the GNU Free Documentation License.
Reply With Quote Quick reply to this message  
Join Date: Oct 2008
Posts: 2,721
Reputation: adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of adatapost has much to be proud of 
Solved Threads: 495
Moderator
adatapost's Avatar
adatapost adatapost is offline Offline
Posting Maven
 
0
  #4
Oct 8th, 2009
IEnumerable, which supports a simple iteration over a collection of a specified type.

  1. protected int GetSpecifiedIndexByName<T>(T collection, string itemName) {
  2. int count = 0;
  3. foreach (MyElement claim in collection as IEnumerable<MyElement>){
  4. if (itemName.Equals(claim.Name)){
  5. return count;
  6. }
  7. count++;
  8. }
  9. return count;
  10. }
Reply With Quote Quick reply to this message  
Join Date: Oct 2009
Posts: 2
Reputation: dhaval_shah is an unknown quantity at this point 
Solved Threads: 0
dhaval_shah dhaval_shah is offline Offline
Newbie Poster
 
-1
  #5
Oct 9th, 2009
Thanks for the replies.

The suggestion I liked

  1. foreach (MyElement claim in collection as IEnumerable<MyElement>){

returns a

System.NullReferenceException : Object reference not set to an instance of an object.

I assume in this case because this is because we don't know what the collection passed in is at runtime?

To put into context what I'm trying to do, I want to have one method which can handle two sets of collections which both extend IEnumerable and both of which collections have their own results elements i.e. in HelloWorldCollection

  1. public MyElement this[int index]
  2. {
  3. get { return (MyElement)ElementRaw(index); }
  4. }

which returns a MyElement object and similarly for FooBarCollection, a FooElement can be returned. Rather than writing two separate methods (or in my case below, one method with two separate for loops dependent on collection type) is there a better way I can implement this? Or, in short, as said earlier, this isn't a good use of generics and shaft this idea completely? Note that HelloWorldCollection and FooBar Collection both extend MyFooCollection.

  1. protected int GetSpecifiedIndexByName<T>(T collection, string itemName)
  2. where T : MyFooCollection
  3. {
  4. int count = 0;
  5. if (collection is HelloWorldCollection) {
  6.  
  7. foreach (MyElement claim in collection)
  8. {
  9. if (itemName.Equals(claim.Name))
  10. {
  11. return count;
  12. }
  13. count++;
  14. }
  15. }
  16.  
  17. else if (collection is FooBarCollection)
  18. {
  19. foreach (FooElement commission in collection)
  20. {
  21. if (itemName.Equals(commission.Name))
  22. {
  23. return count;
  24. }
  25. count++;
  26. }
  27. }
  28.  
  29. throw new ApplicationException("Type with name " + itemName + " not found");
  30. }
Last edited by dhaval_shah; Oct 9th, 2009 at 8:08 am. Reason: Additional notes on thread
Reply With Quote Quick reply to this message  
Reply

Message:


Thread Tools Search this Thread



Tag cloud for C#
About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC