Silverlight C# 3.0 Features
The .NET framework version Silverlight targets is the same that C# 3.0 targets and so we are able to take advantage of the new language features in C# 3.0. The LINQ (Language INtegrated Query) features of C# 3.0 has gotten the most attention of any of the new features, but some of the other features are my favorite additions to the language. I will a link to learning more about LINQ at the end of this post.
Let's start looking at the feature in C# 3.0 by looking at the code listing from my previous post. Here it is as I wrote it:
C# without 3.0 Features
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace ManagedSilverlight101
{
public partial class Page : Canvas
{
public void Page_Loaded(object o, EventArgs e)
{
// Required to initialize variables
InitializeComponent();
parentCanvas = (Canvas)FindName("parentCanvas");
this.ParentCanvas.Background = new SolidColorBrush(Color.FromRgb(255, 0, 0));
Ellipse childShape = new Ellipse();
childShape.Width = 100;
childShape.Height = 100;
LinearGradientBrush lgb = new LinearGradientBrush();
GradientStop gs1 = new GradientStop();
gs1.Color = Color.FromRgb(0, 0, 255);
lgb.GradientStops.Add(gs1);
GradientStop gs2 = new GradientStop();
gs2.Color = Color.FromRgb(0, 255, 0);
gs2.Offset = 1;
lgb.GradientStops.Add(gs2);
childShape.Fill = lgb;
this.parentCanvas.Children.Add(childShape);
}
private Canvas parentCanvas;
public Canvas ParentCanvas
{
get { return parentCanvas; }
set { parentCanvas = value; }
}
}
}
There is nothing particularly special about the previous code listing, but a few item's to notice. First in the Page_Loaded method it is difficult to descern what the intent of the programmer was. Unlike Xaml where everything is declarative the code above is very procedural. All that is happening of course is that the program is displaying a red rectangle containing a small circle with a blue to green gradient color. Something like this:
Result
This was so non obvious from the code above I original thought we were trying to display a red rectangle containing a smaller rectangle with a blue to green gradient color. My eyes scanned the program above and failed to notice that the code was creating and Ellipse instead of a rectangle.
If had tried to achieve this same result with xaml the code would look this this:
<Canvas x:Name="parentCanvas"
xmlns="https://schemas.microsoft.com/client/2007"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Loaded="Page_Loaded"
x:Class="ManagedSilverlight101.Page;assembly=ClientBin/ManagedSilverlight101.dll"
Width="640"
Height="480"
Background="Red"
>
<Ellipse Width="100" Height="100">
<Ellipse.Fill>
<LinearGradientBrush>
<GradientStop Color="Blue"/>
<GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
This is much easier to quickly read and understand what the program will do. The problem of course is that often times we don't have the ability to statically define our display because of the dynamic nature of a given program. This is where the features in C# 3.0 come into play. Below is a code listing that I have transformed from our original code to instead use the new features of the language.
C# 3.0 Style
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
namespace ManagedSilverlight101
{
public partial class Page : Canvas
{
public void Page_Loaded(object o, EventArgs e)
{
// Required to initialize variables
InitializeComponent();
this.ParentCanvas = (Canvas)FindName("parentCanvas");
this.ParentCanvas.Background = new SolidColorBrush(Color.FromRgb(255, 0, 0));
var stops = new GradientStopCollection {
new GradientStop { Color = Color.FromRgb(0, 0, 255) },
new GradientStop { Color = Color.FromRgb(0, 255, 0), Offset = 1 } };
var lgb = new LinearGradientBrush() { GradientStops = stops };
var childShape = new Ellipse { Width = 100, Height = 100, Fill = lgb };
this.ParentCanvas.Children.Add(childShape);
}
public Canvas ParentCanvas { get; set; }
}
}
We have reduced the length of our program and increased it's readability. It in fact is very declarative like the Xaml. Let's examine the new features of C# 3.0 I am using in this example. First after the call to the method InitializeComponent we are assigning the property ParentCanvas from the result of the FindName. This code on it's own isn't any different, but if our eyes scan to the bottom of the Page class we have the line:
public Canvas ParentCanvas { get; set; }
This line is defining a new property named Canvas. Notice that we didn't have to create a separate field to hold the data that is being set by the ParentCanvas property. This is a great shortcut to creating properties which is a good programming practice since in the feature we can change how the data is stored or retrieved without breaking the Page interface.
The next change to our program is the statement:
var stops = new GradientStopCollection { new GradientStop { Color = Color.FromRgb(0, 0, 255) },
new GradientStop { Color = Color.FromRgb(0, 255, 0), Offset = 1 } };
Implicitly Typed Local Variables
This is using a few new features of C#. The first is this part of the statement:
var stops = new GradientStopCollection
We are creating a variable named stops which is of type GradientStopCollection. It is often cumbersome to write the name of the type on both sides of an assignment statement. By placing the var keyword before the name of our variable we are telling the compiler to infer the type information for stops from the type on the right hand side of the assignment operation. In this case a GradientStopCollection.
Unlike JavaScript or other loosely typed languages the local variable stops is strongly typed. Indeed if we were to say something like this after the creating of the stops variable:
stops = "Hello";
The compiler will throw the error: Cannot implicitly convert type 'string' to 'System.Windows.Media.GradientStopCollection'. This is because stops is in fact an instance of a GradientStopCollection. The var keyword is simply a shorthand convenience.
Collection Initializers
The next C# feature is in this part of that statement:
new GradientStopCollection { ... };
It is creating a new GradientStopCollection and then calling Add() passing each element in between the curly braces. A simpler example looks like this:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
and is semantically the same as this code:
List<int> digits = new List<int>();
digits.Add(0);
digits.Add(1);
digits.Add(2);
digits.Add(3);
digits.Add(4);
digits.Add(5);
digits.Add(6);
digits.Add(7);
digits.Add(8);
digits.Add(9);
And if fact this is what the compiler is generating for us when we use this syntax. It is called a collection initializer is a natural extension to how we were already able to assign items to an array:
int [] digits = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Object Initializers
The last new feature in that same statement applies to the elements we are adding to the collection:
new GradientStop { Color = Color.FromRgb(0, 0, 255) },
new GradientStop { Color = Color.FromRgb(0, 255, 0), Offset = 1 }
Here we are using a feature called Object Initializers. Just like a Collection Initializers we are using a curly brace instead of a parentheses after:
new GradientStop {
Then we are have some code that looks like this:
Color = Color.FromRgb(0, 255, 0), Offset = 1
If this looks like property assignment that's because it is. We are simply setting the Color and Offset properties of the GradientStop objects we are creating. That produces much cleaner code then the alternative syntax:
GradientStop gs2 = new GradientStop(); gs2.Color = Color.FromRgb(0, 255, 0); gs2.Offset = 1;
In fact when we combine all three of those features into one statement we are left much easier to read code:
var stops = new GradientStopCollection { new GradientStop { Color = Color.FromRgb(0, 0, 255) },
new GradientStop { Color = Color.FromRgb(0, 255, 0), Offset = 1 } };
Compared to:
LinearGradientBrush lgb = new LinearGradientBrush();
GradientStop gs1 = new GradientStop();
gs1.Color = Color.FromRgb(0, 0, 255);
lgb.GradientStops.Add(gs1);
GradientStop gs2 = new GradientStop();
gs2.Color = Color.FromRgb(0, 255, 0);
gs2.Offset = 1;
lgb.GradientStops.Add(gs2);
Also notice that we didn't have come up with local variable names for our GradientStops. Which also makes for easier to maintain code.
The last two changes to our program are just making use of the same new C# features described above:
var lgb = new LinearGradientBrush() { GradientStops = stops };
var childShape = new Ellipse { Width = 100, Height = 100, Fill = lgb };
Conclusion
As you have seen some of the new features is C# 3.0 make code much more readable and hopefully maintainable. There are a lot of other exciting features in both C# and VB thanks to LINQ. If you are interested in learning about all other great new features check out this great MSDN article on LINQ by Anders Hejlsberg and Don Box.
Comments
- Anonymous
June 05, 2007
During the weekend I spent some minutes to collect some of the greatest Silverlight examples. Most of