11

C#, VS 2010. Somebody, please explain why I can't use var in my code below!

var props = TypeDescriptor.GetProperties(adapter);

// error CS1061: 'object' does not contain a definition for 'DisplayName'
foreach (var prop in props)
{
    string name = prop.DisplayName;
}

// No error
foreach (PropertyDescriptor prop in props)
{
    string name = prop.DisplayName;
}

TypeDescriptor.GetProperties returns a PropertyDescriptorCollection with instances of PropertyDescriptor. Why can't the compiler see this?

l33t
  • 18,692
  • 16
  • 103
  • 180
  • 1
    If you replace var with PropertyDescriptorCollection does it work? – CR41G14 Feb 14 '13 at 11:44
  • I'm guessing `PropertyDescriptorCollection` only implements `IEnumerable`, not `IEnumerable`, which throws the type inference. Why the latter works I have no idea though. – millimoose Feb 14 '13 at 11:47
  • Thats one of the cases where i wouldn't use var. I only use var when the type before the name would be redundant. Otherwise it hides stuff like this. – dowhilefor Feb 14 '13 at 11:47
  • 2
    @millimoose Because foreach will cast every instance and throws an invalidcastexception if it couldn't cast it. – dowhilefor Feb 14 '13 at 11:48
  • A side note, on a matter of taste and readability: `var props = TypeDescriptor.GetProperties(adapter);` causes the reader to have to know or find out the type of the `var`, rather than denote the type explicitly which would be more sensible, and use `var` where the type is obvious (i.e. `var thing = new ExplicitThing()`). – Grant Thomas Feb 14 '13 at 11:49
  • @dowhilefor On the other hand, using an explicit type inside `foreach` hides a cast that can fail at runtime. Not sure what is worse, the hidden cast or the unclear type. – CodesInChaos Feb 14 '13 at 11:50
  • @dowhilefor Ah, I didn't know that. (I'm a Java expatriate and their equivalent of `foreach` doesn't work that way.) – millimoose Feb 14 '13 at 11:56
  • @GrantThomas That's a somewhat pedantic point, seeing as "finding out" can be done by mousing over `prop` in Visual Studio. I wouldn't avoid `var` for that reason much like I don't avoid ambiguity that's resolved by syntax highlighting. – millimoose Feb 14 '13 at 11:59
  • @CodesInChaos i'm sorry that wasn't clear of me, i didn't meant the cast in the foreach, i meant the first var. Usually i use var when the type is absolutely clear like with new and casts. Also if i have a list of elements, where the type is clearly visible i use var in a foreach. – dowhilefor Feb 14 '13 at 12:07
  • @millimoose Considering putting my hand on my mouse is actually an effort detracting from my productivity, let alone having to archaically drag it over the screen, no, that's an added process one can do without. – Grant Thomas Feb 14 '13 at 12:14
  • @millimoose (to your first comment) Technically, it's the public method `GetEnumerator` which is examined to get the element type of the `foreach` in this case, so interfaces are not considered when a public `GetEnumerator()` method exists. See my new answer for details. – Jeppe Stig Nielsen Feb 16 '13 at 22:13

5 Answers5

23

TypeDescriptor.GetProperties returns a class that only has a GetEnumerator implementation that returns the non-generic IEnumerator. The type of its Current property is object - and that's the only type the compiler can infer, so that's the type your prop variable will be.

The second foreach that uses PropertyDescriptor instead of var as the type of prop actually performs a conversion from object to PropertyDescriptor.

Assume this code:

PropertyDescriptor x = // get from somewhere;
object o = x;
PropertyDescriptor y = (PropertyDescriptor)o;

The same is happening in the second foreach loop for each item.


You could add a Cast<PropertyDescriptor>() to get a generic IEnumerable<T> with an implementation of GetEnumerator that returns IEnumerator<T>. If you do this, you can use var in the foreach loop:

var props = TypeDescriptor.GetProperties(adapter).Cast<PropertyDescriptor>();

// No error
foreach (var prop in props)
{
    string name = prop.DisplayName;
}
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • 3
    To be very pedantic, before the compiler even cares about `IEnumerable` and `IEnumareble`, it checks if there exists a public instance method `GetEnumerator()`, and if that method would work (return type must have some specific members). This is the case here. So actually it is the return type of the public method which makes `var` mean `object`, and the interfaces are not considered. (It's easy to write a class which implements only non-generic `IEnumerable` (explicit interface implementation) but whose public `GetEnumerator()` method returns something type-safe.) – Jeppe Stig Nielsen Feb 14 '13 at 12:24
  • @JeppeStigNielsen: Thanks for your comment. I tried to integrate it in my answer, please check. – Daniel Hilgarth Feb 14 '13 at 15:27
  • Great. I checked it and decided to make a minor edit to your answer so hopefully now it's a little better. Maybe I will write my own answer with more details. – Jeppe Stig Nielsen Feb 16 '13 at 21:02
  • (Now I did write an answer like that.) – Jeppe Stig Nielsen Feb 16 '13 at 22:17
8

PropertyDescriptorCollection only implements IEnumerable, so the compiler only knows that elements contained in it are of type object. When you specify the type in your foreach loop, the compiler will insert a cast to the type you specify.

You can use the Cast<T> extension method on IEnumerable to cast each item to a given type:

using System.Linq;
...
IEnumerable<PropertyDescriptor> descriptors = props.Cast<PropertyDescriptor>();
Lee
  • 142,018
  • 20
  • 234
  • 287
5

Because TypeDescriptor.GetProperties returns a PropertyDescriptorCollection which does not implement IEnumerable<PropertyDescriptor> but only IEnumerable.

Therefore prop is just an object which has no DisplayName property at compile time.

So you have to specify the type explicitely in the foreach:

foreach (PropertyDescriptor prop in props)
{
    string name = prop.DisplayName;
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
3

PropertyDescriptorCollection implements IEnumerable but not IEnumerable<PropertyDescriptor> so all that can be seen is that it enumerates objects. So that's what var infers.

foreach has always had the ability to perform an hidden cast to whatever its iteration variable type is, so that's why the second version works. It had to work this way in the pre-generics era.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
2

What I am going to cover here is described in formal detail in the C# Language Specification, section "The foreach statement".

When you use an implicitly typed iteration variable in a foreach statement (that is, var), which is a very good thing to do, here's how the compiler finds out what var is to mean.

First it checks the compile-time type of the expression to the right-hand side of the in keyword in your foreach. (If it's an array type like PropertyDescriptor[] or PropertyDescriptor[,,,] or similar, a special rule applies. There's another rule if it's dynamic.)

It checks if that type has a method called precisely GetEnumerator (with that capitalization) with an overload that is public, non-static, non-generic and takes in zero parameters. If so, it examines the return type of this method (we're still talking compile-time types here, so it's the declared return type). This type must have a method MoveNext() and a property Current. It then takes the property type of Current and uses that as the element type. So your var stands for this type.

To show how this works, I wrote this:

    class Foreachable
    {
        public MyEnumeratorType GetEnumerator()  // OK, public, non-static, non-generic, zero arguments
        {
            return default(MyEnumeratorType);
        }

    }

    struct MyEnumeratorType
    {
        public int Current
        {
            get { return 42; }
        }

        public bool MoveNext()
        {
            return true;
        }
    }

    static class Test
    {
        static void Main()
        {
            var coll = new Foreachable();

            foreach (var x in coll)   // mouse-over 'var' to see it translates to 'int'
            {
                Console.WriteLine(x);
            }
        }
    }

You see that var becomes int (System.Int32) in my case because of the type of the Current property.

Now in your case the compile-time type of props is PropertyDescriptorCollection. The type has a GetEnumerator() which is public and non-static as required. The return type of the method is seen to be System.Collections.IEnumerator in this case. This IEnumerator type has the required Current property, and the type of this property is seen to be Object. So there it comes from!

(A lot of class originally written for .NET 1 have this design. No strong typing with foreach on them.)

Note that if a type implements IEnumerable<out T> (generic) or/and IEnumerable (non-generic), and if one of these two interfaces is implemented "implicitly" (normally, not explicit interface implementation), then the type surely has a GetEnumerator which is public and non-static, non-generic and take zero arguments. So that public method will be used by foreach.

Now if the compile-time type of the expression you're trying to foreach does not have a public instance method GetEnumerator() (no type parameters and no value parameters), the compiler will see if the type is converible to IEnumerable<Something> or (else) to IEnumerable. Since IEnumerable<out T> is covariant in T, there will often be many Something so that IEnumerable<Something> applies. This is explained a little confusingly in the spec (version 5.0). For it is also possible that the type looks like this:

    class Foreachable : IEnumerable<Animal>, IEnumerable<Giraffe>
    {
        // lots of stuff goes here
    }

for reference types Animal and Giraffe with the expected inheritance relation, and it is not clear from the spec version 5.0 that this class (compile-time type) can not be foreached.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181