tags:

views:

96

answers:

4
foreach (var item in mainCanvas.Children)
{
    if (item is Button)
    {
        (item as Button).Content = "this is a button";
    }                
}

Can I use LINQ or other feature of .NET 4 to be more concise (maybe performant)?

+11  A: 

You can use Enumerable.OfType:

foreach (var button in mainCanvas.Children.OfType<Button>())
{
    button.Content = "this is a button";
}

Performance Measurements

Method 1: OPs original suggestion

foreach (var item in mainCanvas.Children)
{
    if (item is Button)
    {
        (item as Button).Content = "this is a button";
    }                
}

Method 2: OfType

foreach (var button in mainCanvas.Children.OfType<Button>())
{
    button.Content = "this is a button";
}

Method 3: Only cast once

foreach (var item in mainCanvas.Children)
{
    Button button = item as Button;
    if (button != null)
    {
        button.Content = "this is a button";
    }                
}

Method 4: for loop:

List<object> children = mainCanvas.Children;
for (int i = 0; i < children.Count; ++i)
{
    object item = children[i];
    if (item is Button)
    {
        (item as Button).Content = "this is a button";
    }                
}

Results

Iterations per second

Method 1: 18539180
Method 2:  7376857
Method 3: 19280965
Method 4: 20739241

Conclusion

  • The biggest improvement can be gained by using a simple for loop instead of foreach.
  • It is also possible to improve performance slightly by casting only once.
  • Using OfType is considerably slower.

But remember to optimize readability first, and only optimize performance if you have performance profiled and found that this specific code is the performance bottleneck.

Mark Byers
Looks like a winner to me - short and fast. Doing stuff with lambdas seems like an overkill.
Hamish Grubijan
Will it be more performant that the initial example version?
serhio
@Mark Byers: Buttons was for the example purpose. In application I have a graph with a huge number of curves. I select them and move...
serhio
I think performance-wise IEnumerable<T>.OfType() will do just as fine as original post. So if you want concise, there is a one-liner solution here, if performance is critical, I believe for-loop performs better than foreach and List.ForEach(), but that is marginal.
danijels
@serhio: The best way to find out which is fastest is to measure it. I've updated my answer with some measurements I've made.
Mark Byers
@Mark, what about the 5th method - the hybrid of method 3 and 4? How much better is it to "cast once"? Also, this might be a dumb idea, but how about the 6th method - using `object` instead of `var` in ... say method 3? What if something like `IButton` is used instead of `Button` when casting? Finally, how do you measure the performance? Are the numbers consistent from run to run? With JIT, pre-emption, noise is possible. Thanks.
Hamish Grubijan
good explained.
serhio
+1  A: 

See if there is an OfType<T> extension.

foreach (var item in mainCanvas.Children.OfType<Button>()) 
{ 
    item.Content = "this is a button"; 
}

If not, you can use:

foreach (var item in mainCanvas.Children.Where(item=>item is Button).Cast<Button>()) 
{ 
    item.Content = "this is a button"; 
}
Danny Chen
If there is not an OfType extension there will also not be a Where or a Cast extension. All 3 methods have existed since the first version of Enumerable class. Also Where cannot be used UIElementCollection as it not generic.
Bear Monkey
+3  A: 

One line should do it

mainCanvas.Children.OfType<Button>.ToList().ForEach(b => b.Content = "this is a button");
danijels
maybe this is concise, but the I doubt about readability... useful however..
serhio
That is always debatable, but this is just to answer the question, which was, after all, about it being concise. Everyone should take whatever flavor suits them.
danijels
Turning an iterator into a List, and then iterating over it is expensive.
Hamish Grubijan
That depends on the contents of .Children. If there is hundreds of items and only 3-4 buttons, this should be a good way. Can't tell here.
danijels
@hamish-grubijan: foreach should do the same thing, isn't it?
serhio
@serhio, `foreach` should operate on an `IEnumerable`, which can be a lazy collection.
Hamish Grubijan
+1  A: 

Not that it's particularly superior, but there is something nice about this syntax:

Using LINQ and the Microsoft ReactiveExtensions framework,

mainCanvas.Children
   .OfType<Button>()
   .Do(b => b.Content = "I'm a button!")
   .Run();
Judah Himango