0

Consider this working code:

    RadComboBox rcb = new RadComboBox(); // Telerik.Web.UI.RadComboBox
    rcb.Items.Add(new RadComboBoxItem("One", "1"));
    rcb.Items.Add(new RadComboBoxItem("Two", "2"));
    rcb.Items.Add(new RadComboBoxItem("Three", "3"));
    // check all items
    foreach (RadComboBoxItem i in rcb.Items)
    {
        i.Checked = true;
    }

If I replace the foreach loop with var, it does not compile:

    RadComboBox rcb = new RadComboBox(); // Telerik.Web.UI.RadComboBox
    rcb.Items.Add(new RadComboBoxItem("One", "1"));
    rcb.Items.Add(new RadComboBoxItem("Two", "2"));
    rcb.Items.Add(new RadComboBoxItem("Three", "3"));
    // check all items
    foreach (var i in rcb.Items)
    {
        i.Checked = true;
    }

The error is:

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

So, I wonder, what are the conditions when you cannot use var?

Edit: Just to clarify, I wasn't simply asking "why doesn't this work"? Here's an example that could make the problem more obvious:

    List<Object> stuff = new List<object>();
    stuff.Add("one");
    stuff.Add("two");
    stuff.Add("three");
    foreach (string s in stuff)
    {
        if (s.Length > 3)
            // do something
    }

Now if you change string s in stuff to var s in stuff, it obviously isn't going to compile. You can't just willy-nilly go and replace any type with var and expect it to compile. The error in my thinking that led me to propose the question in the first place was that I assumed that since rcb.Items is of type RadComboBoxItemCollection, that the enumerable type would be RadComboBoxItem, and this is not the case.

Unfortunately my example tainted the question by leading some of the answers down the "why does substituting a type with var not work?" path. Paulo actually answered with what I was looking for (others partially did too), which is why I have awarded it the best answer.

TTT
  • 22,611
  • 8
  • 63
  • 69
  • @TTT What is the type of rcb.Items ? – Virus Jul 02 '14 at 11:38
  • @Virus - RadComboBoxItemCollection – TTT Jul 02 '14 at 11:39
  • 1
    you can help C# there by using `.Cast()` in your foreach loop (http://msdn.microsoft.com/de-de/library/bb341406(v=vs.110).aspx) – Random Dev Jul 02 '14 at 11:40
  • @OblTobl - oh wow, I searched for a few minutes and couldn't find that. I even re-read the msdn doc for var and didn't see anything about it. Am I being downvoted because I only spent 5 minutes trying to find the answer rather than longer? Why doesn't this page list the requirement: http://msdn.microsoft.com/en-us/library/bb384061.aspx – TTT Jul 02 '14 at 11:53
  • @TTT the answer is even proposed when you are just trying to type the title of your question ;-) – Obl Tobl Jul 02 '14 at 11:59
  • 1
    @OblTobl I must have missed it. Though I do see different answers here than the duplicate question, so I'm still glad I asked it because I learned some things here that I wouldn't have from the duplicate question. – TTT Jul 02 '14 at 12:07
  • 1
    Note that this particular question has not been answered before. Previous versions of [`RadComboBoxItemCollection`](http://www.telerik.com/help/aspnet-ajax/t_telerik_web_ui_radcomboboxitemcollection.html) didn't implement `IEnumerable`, only the non-generic `IEnumerable`. – acelent Jul 02 '14 at 12:35

5 Answers5

5

You can use var any time you want to use implicit typing. That is, when the type can be derived from the declaration. It would seem in this case that the actual type of Items is a collection of objects. So that's all var knows in that declaration.

By explicitly declaring the type as RadComboBoxItem you are essentially doing something very similar to this (but with more compile-time checking):

foreach (var i in rcb.Items)
{
    (i as RadComboBoxItem).Checked = true;
}

The object instances in Items can be implicitly converted to RadComboBoxItem, but var doesn't know to do that. You have to explicitly do that. (Personally I would consider that an odd design of RadComboBox, but it could be a holdover from earlier design decisions. Collections in .NET weren't always as strongly-typed as they are today. Things like ArrayList used to cause similar problems.)

David
  • 208,112
  • 36
  • 198
  • 279
2

The reason this is occurring in that for loop is because the compiler is unable to infer the type of the enumerable object. This occurs in older code that predates generics - with generics, the compiler is able to infer that the next item from the iterator will be of type T. However, without generics, we return a non-generic IEnumerator type, whose iterations only expose an object.

Remember that a for loop is basically syntactic sugar for invoking the Next() method on an enumerator, and then checking the Current property on aforementioned enumerator. Older types that pre-date generics did not have generics in their Enumerator, so they would always return an object (here is the actual method invoked).

The newer, strongly-typed enumerators however return a type of T when you call Current, so the type can be inferred.

Another example of this is the System.Data namespace when trying to iterate over a DataTable's rows.

In this case you should explicitly state the type of the variable.

I appreciate this might be long winded but I was trying to provide the reason why the type can't be inferred here and a little background because I was confused when I first had the issue trying to enumerate a DataRowCollection.

TLDR: you're iterating over a non-generically typed enumerator when you do for. Because it is not generically typed, the enumerator returns instances of Object. Ergo, you get object instances back and can only use the methods available to Object until you cast the variable.

Dan
  • 10,282
  • 2
  • 37
  • 64
  • 3
    to be precise `RadComboBoxItemCollection`most likely only implement `IEnumerable` and not `IEnumerabe` – Random Dev Jul 02 '14 at 11:42
  • 1
    Basically what I'm trying to say, yeah, is that RadComboBoxItemCollection is not generically-typed. We actually use Telerik at my work place. – Dan Jul 02 '14 at 11:43
  • If you do you should sent them a request ... we have this since C# 2.0 I think ;) – Random Dev Jul 02 '14 at 11:45
  • 1
    When I say "we", I mean our old software uses Telerik. We're now migrating to things that aren't older than my high school career :) – Dan Jul 02 '14 at 11:45
1

As rcb.Items returns a RadComboBoxItemCollection, i will become of type object.

Either you need to convert it to a RadComboBoxItem

(i as RadComboBoxItem).Checked = true;

Or modify the loop like

foreach (var i in rcb.Items.Cast<RadComboBoxItem>())
{
    i.Checked = true;
}

Hope it helps.

Virus
  • 2,445
  • 1
  • 14
  • 17
1

The RadComboBox Items property will return a RadComboBoxItemCollection, which implements both IEnumerable<RadComboBoxItem> and the non-generic IEnumerable.

Although I'm not familiar with Telerik's products, I suspect previous versions didn't implement IEnumerable<RadComboBoxItem>.

According to the C# language specification, it should work (C# >= 3.0 section 8.8.4), in a simpler English:

  • If the expression's type is an array, it's cast to IEnumerable and the element type is the array's element type
  • (C# >= 4.0) If the expression's type is dynamic, it's cast to IEnumerable and the element type is dynamic if var is used, object otherwise
  • If the expression's type quacks like IEnumerable and the quacked enumerator quacks like IEnumerator, the element type is the type of the Current property of the quacked enumerator
    • Quacking like IEnumerable means it has:
      • A public non-static unambiguous GetEnumerator method with a class, struct or interface return type
    • Quacking like IEnumerator means it has:
      • A public non-static Current property
      • A public non-static unambiguous MoveNext method with a bool return type
    • If any condition is not met once a GetEnumerator method is found, an error is produced
  • If the expression's type implements a single IEnumerable<T>, the element type is T
  • If the expression implements more than one generic IEnumerable<T>, an error is produced
  • If the expression implements the non-generic IEnumerable, the element type is object
  • Otherwise, an error is produced

Your case should fall in the emphasized bullet given the current version.

However, if you're using a previous version and my guess is right, your case is the second to last.

acelent
  • 7,965
  • 21
  • 39
0

Using var is like using type Object in that case since Items is a collection of Object.
If you wish to use the expression i.Checked = true, you must transtype your i into a RadComboBoxItem first.

foreach(RadComboBoxItem i in rcb.Items)
{
    i.Checked = true;
}

This would be the best if you wish to use this argument (except if rct.Items contains something else than RadComboBoxItem types, in that case you may have to find the right interface.

Hybris95
  • 2,286
  • 2
  • 16
  • 33
  • 1
    That is incorrect - using `var` will perform compile-time type-inference to work out the appropriate compile-time type, which will typically *not* be `Object`. – RB. Jul 02 '14 at 11:38