8

More specifically, why does this work:

foreach (ChangeSetEntry changeRow in changeSet.ChangeSetEntries)
    if (changeRow is RouteStage)
    { ... }

but this not?

ChangeSetEntry changeRow = changeSet.ChangeSetEntries[0];
if (changeRow is RouteStage)
{ ... }

In the latter case, I get a compiler warning saying:

The given expression is never of the provided type.

I can understand that, as changeRow is a ChangeSetEntry not a RouteStage, so why does it work inside the foreach block?

This is in my override of the Submit method in an RIA Services DomainService. RouteStage is an entity I have defined to be returned by the DomainService.

Brian
  • 5,069
  • 7
  • 37
  • 47
john_cat
  • 354
  • 2
  • 8
  • 3
    It might be giving you a warning because `changeSet.ChangeSetEntries[0]` might an argument out of range exception, whereas the `foreach` will simply not execute if there are no objects it the collection, so the `is` line will never get called. – Tim Jan 02 '14 at 16:22
  • The answer is in the definition of the `ChangeSetEntries` property – Federico Berasategui Jan 02 '14 at 16:22
  • 6
    Ah you *sure* the two code blocks are referring to the same types? Can you post a short but complete program demonstrating the problem? – Jon Skeet Jan 02 '14 at 16:22
  • I assume the warning is on the `if` line, by the way? – Jon Skeet Jan 02 '14 at 16:23
  • I'd expect both snippets to work. I seem to remember implementing it in several projects. I usually use it for shorthand when upcasting (instead of convert, catch exception if thrown, etc) – Flater Jan 02 '14 at 16:23
  • @JonSkeet I'm `guessing` that the `ChangeSetEntries` collection might be of type `IEnumerable`, instead of `IEnumerable`. What do you think? – Federico Berasategui Jan 02 '14 at 16:23
  • 1
    @Tim: No, you'd still get the same warning. I suspect this is a matter of some type existing in two different namespaces. – Jon Skeet Jan 02 '14 at 16:23
  • Does it have to do with the implementation of IEnumerable? You can also create a foreach with at the left side a type that can't be in the list. – Patrick Hofman Jan 02 '14 at 16:23
  • 1
    @HighCore: That wouldn't affect the `is` check. The type of `changeRow` is the same in each case. – Jon Skeet Jan 02 '14 at 16:24
  • @JonSkeet but doing `foreach` over an `IEnumerable` (non-generic one) will return `object`, therefore the compiler has no way to know that will "never be of the provided type" – Federico Berasategui Jan 02 '14 at 16:25
  • It does not work, it just doesn't give you a warning, most likely because it would only be processed at run time. – Bit Jan 02 '14 at 16:26
  • 1
    I can't repeat this problem with VS2013, the warning is there in both cases with all typed collection types I could think of trying. – Joachim Isaksson Jan 02 '14 at 16:34
  • 1
    I can't reproduce. I tested with the exact same case you described (override of Submit in a RIA domain service class). Both are producing the same warning. – ken2k Jan 02 '14 at 16:36
  • 2
    @HighCore: The compiler does have a way to know that, because we declare the type of the variable in the `foreach` loop. So the variable is strongly typed. – StriplingWarrior Jan 02 '14 at 16:44
  • 1
    @HighCore All of that is irrelivant; the result of the `IEnumerable` is cast to `ChangeSetEntry` by the `foreach` loop, so in both cases you have a variable of type `ChangeSetEntry`. How you go about getting that variable or populating it isn't relevant in this context. If the type check is known to fail at compile time for one, it'll be known to fail for the other, unless either the variables are representing different types with the same name, or `RouteStage` means something different in the two contexts. – Servy Jan 02 '14 at 16:45
  • @servy you're right. I completely missed that point. – Federico Berasategui Jan 02 '14 at 16:47
  • @JonSkeet: Yes, I'm sure the two code blocks are referring to the same type. One is a modified version of the other. I haven't changed my using statements at all or modified anything outside this method. – john_cat Jan 02 '14 at 17:18
  • Can you produce a short but complete program demonstrating the problem? It does sound very odd. – Jon Skeet Jan 02 '14 at 17:19
  • @HighCore: ChangeSetEntries is of type ReadOnlyCollection – john_cat Jan 02 '14 at 17:19
  • OK, it's just me being stupid. The line should read: if (changeRow.Entity is RouteStage) then it compiles cleanly. Sorry! Thanks for your help, everyone. – john_cat Jan 02 '14 at 17:28

3 Answers3

1

I tested your code examples (exact same conditions you described, in the Submit override of a RIA domain service), both are producing a warning.

It's actually expected: in both cases you have changeRow that is declared as a ChangeSetEntry variable. Whether it's in a foreach loop or not doesn't change anything about that. And as RouteStage doesn't inherit from ChangeSetEntry (which is sealed), the warning should always be displayed.

Either you oversimplified your example (and something is missing), or as Jon Skeet suspected, the RouteStage type in both code snippets doesn't actually refer to the same type (check you don't have using RouteStage = ChangeSetEntry; somewhere in your class).

ken2k
  • 48,145
  • 10
  • 116
  • 176
0

The line should read:

if (changeRow.Entity is RouteStage)

...then it compiles cleanly. One of those cases of "can't see for looking". TFS highlighted what I had inadvertently changed.

john_cat
  • 354
  • 2
  • 8
-1

I can understand that, as changeRow is a ChangeSetEntry not a RouteStage, so why does it work inside the foreach block?

I don't think that it works in foreach loop. The only difference seems to be that compiler doesn't perform static check on variables inside the loop. Hence, no warning displayed. But I suppose that execution never passes if within the foreach loop because the condition is always false.

Way out of this is just to remove the if statement in both forms, because it is known in advance to never be true.

Irvin Dominin
  • 30,819
  • 9
  • 77
  • 111
Zoran Horvat
  • 10,924
  • 3
  • 31
  • 43
  • The expression does evaluate to 'true' at runtime inside the foreach block. I ran into the compiler warning when I tried to move the check outside the foreach block. – john_cat Jan 02 '14 at 17:02