Consider the following ASP.NET Razor sample:
<div class="content">
@foreach (var item in Model
.Items
.Where(x => x.Name != null)
.OrderByDescending(x => x.Added))
{
<div class="item">
@item.Name
</div>
}
</div>
If one wishes to check that the collection would first return items it requires adding another level of testing to the Razor:
<div class="content">
@if (Model.Items.Count(x => x.Name != null) > 0)
{
foreach (var item in Model
.Items
.Where(x => x.Name != null)
.OrderByDescending(x => x.Added))
{
<div class="item">
@item.Name
</div>
}
}
else // We didn't actually find anything
{
<div class="none">
No items
</div>
}
</div>
You could propose two major solutions currently supported by the language to solve this issue:
First: create an intermediary variable in the view:
<div class="content">
@{
var filteredCollection = Model.Items.Where(x => x.Name != null);
if (filteredCollection.Count() > 0)
{
foreach (var item in filteredCollection.OrderByDescending(x => x.Added))
{
<div class="item">
@item.Name
</div>
}
}
else // We didn't actually find anything
{
<div class="none">
No items
</div>
}
}
</div>
Obviously this is not a pure solution: our view contains a local variable and requires a local variable if using this solution for each and every location we wish to operate on a collection in this manner with.
The second solution would be to add the filteredCollection
to the model being passed to the view, then simply:
<div class="content">
@if (Model.ItemsWithName.Count() > 0)
{
foreach (var item in Model.ItemWithName.OrderByDescending(x => x.Added))
{
<div class="item">
@item.Name
</div>
}
}
else // We didn't actually find anything
{
<div class="none">
No items
</div>
}
</div>
This increases maintenance with the model, and requires this to be somehow derived from the model. (If it's a simple filter on another collection in the model that's easy enough, but the more complex our conditions become the more maintenance on the model is required.)
I propose we add an else
block to be added directly after a foreach
block which would only execute if no items were iterated.
<div class="content">
@foreach (var item in Model
.Items
.Where(x => x.Name != null)
.OrderByDescending(x => x.Added))
{
<div class="item">
@item.Name
</div>
}
else // We didn't actually iterate anything
{
<div class="none">
No items
</div>
}
</div>
This would be most helpful in ASP.NET applications where custom object sorting and filtering is more likely to be common and the potential to display a 'no items to display' message is much higher.
I've had the same wish, and in the same circumstances. I wonder if it could just be Razor sugar or if it would really be helpful in C# itself.
Razor could insert a local bool which gets set each time the loop iterates, and generate an if (!iterated)
block instead of the else
keyword.
Dupe of #134.
Even if reconsidered I think that there are ambiguity concerns to consider. The following is already legal syntax:
if (condition)
foreach (var item in array) {
// do something
}
else {
// do something else
}
Can you link the actual dupe?
@jnm2 Oops, typo. #134 (I'll fix the original comment too)
@HaloFour This isn't a dupe if it's implemented as Razor sugar
@jnm2 Then why it needs to be implemented at the language? they can add a special syntax and deal with it themselves.
@eyalsk
Then why it needs to be implemented at the language? they can add a special syntax and deal with it themselves.
Correct, it would go in a different repo.
There's no such thing as "razor sugar". Razor passes code sections as-is to the generated view class.
@alrz Razor is sugar 馃槅
This doesn't seem that complex to add to the parser that separates C# and HTML.
For a lot of cases you can use the extension method DefaultIfEmpty(), where you can provide an element to return if the IEnumerable is empty.
We should just let else + any keywords format in the same line. And you should swap logic for empty on top
C#
if(array.IsNullOrEmpty())
{
// default logic
}
else foreach(var obj in array)
{
// iter logic
}
It already valid syntax (need to add IsNullOrEmpty
extention method) just formatter make it annoying separate line
On a second thought. if no one mind this syntax just it conflict with valid syntax we can just tweak any syntax
```C#
foreach if(var foo in bar)
{
Console.WriteLine(foo);
}
else Console.WriteLine("no foos in the bar");
// or
if foreach(var foo in bar)
{
Console.WriteLine(foo);
}
else Console.WriteLine("no foos in the bar");
// or for
for(var foo in bar) // introduce in to work in for in addition to ;;
{
Console.WriteLine(foo);
}
else Console.WriteLine("no foos in the bar");
// or colon
for(var foo : bar)
{
Console.WriteLine(foo);
}
else Console.WriteLine("no foos in the bar");
```
Wow! What an elegant solution! I love this proposal. I've always just resorted to doing something like having an extra count variable and then IFing off it. I really like this suggestion a lot.
If this is common in Razor, you could use a partial view or even an action result filter. If there is a use case outside ASP.NET, that could be also addressed via similar mechanisms, depending on problem domain.
I'll move this to csharplang (where language design is now discussed). Thanks
Issue moved to dotnet/csharplang #1025 via ZenHub
Most helpful comment
Even if reconsidered I think that there are ambiguity concerns to consider. The following is already legal syntax: