Chapter 16. Language-Specific Features

WHAT'S IN THIS CHAPTER?

  • Choosing the right language for the job

  • Working with the new C# and VB language features

  • Understanding and getting started with Visual F#

The .NET language ecosystem is alive and well. With literally hundreds of languages targeting the .NET Framework (you can find a fairly complete list at www.dotnetpowered.com/languages.aspx), .NET developers have a huge language arsenal at their disposal. Because the .NET Framework was designed with language interoperability in mind, these languages are also able to talk to each other, allowing for a creative cross-pollination of languages across a cross-section of programming problems. You're literally able to choose the right language tool for the job.

This chapter explores some of the latest language paradigms within the ecosystem, each with particular features and flavors that make solving those tough programming problems just a little bit easier. After a tour of some of the programming language paradigms, you learn about some of the new language features introduced in Visual Studio 2010, including one of the newest additions to Microsoft's supported language list: a functional programming language called F#.

HITTING A NAIL WITH THE RIGHT HAMMER

We need to be flexible and diverse programmers. The programming landscape requires elegance, efficiency, and longevity. Gone are the days of picking one language and platform and executing like crazy to meet the requirements of our problem domain. Different nails sometimes require different hammers.

Given that hundreds of languages are available on the .NET platform, what makes them different from each other? Truth be told, most are small evolutions of each other, and are not particularly useful in an enterprise environment. However, it is easy to class these languages into a range of programming paradigms.

Programming languages can be classified in various ways, but I like to take a broad-strokes approach, putting languages into four broad categories: imperative, declarative, dynamic, and functional. This section takes a quick look at these categories and what languages fit within them.

Imperative

Your classic all-rounder — imperative languages describe how, rather than what. Imperative languages were designed from the get-go to raise the level of abstraction of machine code. It's said that when Grace Hopper invented the first-ever compiler, the A-0 system, her machine code programming colleagues complained that she would put them out of a job.

It includes languages where language statements primarily manipulate program state. Object-oriented languages are classic state manipulators through their focus on creating and changing objects. The C and C++ languages fit nicely in the imperative bucket, as do our favorites VB and C#.

They're great at describing real-world scenarios through the world of the type system and objects. They are strict — meaning the compiler does a lot of safety checking for you. Safety checking (or type soundness) means you can't easily change a Cow type to a Sheep type — so, for example, if you declare that you need a Cow type in the signature of your method, the compiler will make sure that you don't hand that method a Sheep instead. They usually have fantastic reuse mechanisms too — code written with polymorphism in mind can easily be abstracted away so that other code paths, from within the same module through to entirely different projects, can leverage the code that was written. They also benefit from being the most popular. They're clearly a good choice if you need a team of people working on a problem.

Declarative

Declarative languages describe what, rather than how (in contrast to imperative, which describes the how through program statements that manipulate state). Your classic well-known declarative language is HTML. It describes the layout of a page: what font, text, and decoration are required, and where images should be shown. Parts of another classic, SQL, are declarative — it describes what it wants from a relational database. A recent example of a declarative language is XAML (eXtensible Application Markup Language), which leads a long list of XML-based declarative languages.

Declarative languages are great for describing and transforming data, and as such, we've invoked them from our imperative languages to retrieve and manipulate data for years.

Dynamic

The dynamic category includes all languages that exhibit "dynamic" features such as late-bound binding and invocation, REPL (Read Eval Print Loops), duck typing (non-strict typing, that is, if an object looks like a duck and walks like a duck it must be a duck), and more.

Dynamic languages typically delay as much compilation behavior as they possibly can to run time. Whereas your typical C# method invocation "Console.WriteLine()" would be statically checked and linked to at compile time, a dynamic language would delay all this to run time. Instead, it looks up the "WriteLine()" method on the "Console" type while the program is actually running, and if it finds it, invokes it at run time. If it does not find the method or the type, the language may expose features for the programmer to hook up a "failure method," so that the programmer can catch these failures and programmatically "try something else."

Other features include extending objects, classes, and interfaces at run time (meaning modifying the type system on the fly); dynamic scoping (for example, a variable defined in the global scope can be accessed by private or nested methods); and more.

Compilation methods like this have interesting side effects. If your types don't need to be fully defined up front (because the type system is so flexible), you can write code that will consume strict interfaces (like COM, or other .NET assemblies, for example) and make that code highly resilient to failure and versioning of that interface. In the C# world, if an interface you're consuming from an external assembly changes, you typically need a recompile (and a fix-up of your internal code) to get it up and running again. From a dynamic language, you could hook the "method missing" mechanism of the language, and when a particular interface has changed simply do some "reflective" lookup on that interface and decide if you can invoke anything else. This means you can write fantastic glue code that glues together interfaces that may not be versioned dependently.

Dynamic languages are great at rapid prototyping. Not having to define your types up front (something you would do straightaway in C#) allows you concentrate on code to solve problems, rather than on the type constraints of the implementation. The REPL (Read Eval Print Loop) allows you to write prototypes line-by-line and immediately see the changes reflected in the program instead of wasting time doing a compile-run-debug cycle.

If you're interested in taking a look at dynamic languages on the .NET platform, you're in luck. Microsoft has released IronPython (www.codeplex.com/IronPython), which is a Python implementation for the .NET Framework. The Python language is a classic example of a dynamic language, and is wildly popular in the scientific computing, systems administration, and general programming space. If Python doesn't tickle your fancy, you can also download and try out IronRuby (www.ironruby.net/), which is an implementation of the Ruby language for the .NET Framework. Ruby is a dynamic language that's popular in the web space, and though it's still relatively young, it has a huge popular following.

Functional

The functional category focuses on languages that treat computation like mathematical functions. They try really hard to avoid state manipulation, instead concentrating on the results of functions as the building blocks for solving problems. If you've done any calculus before, the theory behind functional programming might look familiar.

Because functional programming typically doesn't manipulate state, the surface area of side effects generated in a program is much smaller. This means it is fantastic for implementing parallel and concurrent algorithms. The holy grail of highly concurrent systems is the avoidance of overlapping "unintended" state manipulation. Dead-locks, race conditions, and broken invariants are classic manifestations of not synchronizing your state manipulation code. Concurrent programming and synchronization through threads, shared memory, and locks is incredibly hard, so why not avoid it altogether? Because functional programming encourages the programmer to write stateless algorithms, the compiler can then reason about automatic parallelism of the code. This means you can exploit the power of multi-core processors without the heavy lifting of managing threads, locks, and shared memory.

Functional programs are terse. There's usually less code required to arrive at a solution than with its imperative cousin. Less code typically means fewer bugs and less surface area to test.

What's It All Mean?

These categories are broad by design: languages may include features that are common to one or more of these categories. The categories should be used as a way to relate the language features that exist in them to the particular problems that they are good at solving.

Languages like C# and VB.NET are now leveraging features from their dynamic and functional counterparts. LINQ (Language Integrated Query) is a great example of a borrowed paradigm. Consider the following C# 3.0 LINQ query:

var query =   from c in customers
              where c.CompanyName == "Microsoft"
              select new { c.ID, c.CompanyName };

This has a few borrowed features. The var keyword says "infer the type of the query specified," which looks a lot like something out of a dynamic language. The actual query itself, from c in ..., looks and acts like the declarative language SQL, and the select new { c.ID ... creates a new anonymous type, again something that looks fairly dynamic. The code-generated results of these statements are particularly interesting: they're actually not compiled into classic IL (intermediate language); they're instead compiled into what's called an expression tree and then interpreted at run time — something that's taken right out of the dynamic language playbook.

The truth is, these categories don't particularly matter too much for deciding which tool to use to solve the right problem. Cross-pollination of feature sets from each category into languages is in fashion at the moment, which is good for a programmer, whose favorite language typically picks up the best features from each category. Currently the trend is for imperative/dynamic languages to be used by application developers, while functional languages have excelled in solving domain-specific problems.

If you're a .NET programmer, you have even more to smile about. Language interoperation through the CLS (Common Language Specification) works seamlessly, meaning you can use your favorite imperative language for the majority of the problems you're trying to solve, then call into a functional language for your data manipulation, or maybe some hard-core math you need to solve a problem.

A TALE OF TWO LANGUAGES

Since the creation of the .NET Framework there has been an ongoing debate as to which language developers should use to write their applications. In a lot of cases, teams choose between C# and VB based upon prior knowledge of either C/C++, Java, or VB6. However, this decision was made harder by a previous divergence of the languages. In the past, the language teams within Microsoft made additions to their languages independently, resulting in a number of features appearing in one language and not the other. For example, VB has integrated language support for working with XML literals, whereas C# has anonymous methods and iterators. Although these features benefited the users of those languages, it made it difficult for organizations to choose which language to use. In fact, in some cases organizations ended up using a mix of languages attempting to use the best language for the job at hand. Unfortunately, this either means that the development team needs to be able to read and write both languages, or the team gets fragmented with some working on the C# and some on the VB code.

With Visual Studio 2010 and the .NET Framework 4.0, a decision was made within Microsoft to co-evolve the two primary .NET languages, C# and VB. This co-evolution would seek to minimize the differences in capabilities between the two languages (often referred to as feature parity). However, this isn't an attempt to merge the two languages; in fact, it's quite the opposite. Microsoft has clearly indicated that each language may implement a feature in a different way to ensure it is in line with the way developers already write and interact with the language.

In the coming sections, you learn about the language features that have been added in Visual Studio 2010. You start by looking at the features common to both languages before going through changes to the individual languages, most of which are discussed in the context of feature parity and how the introduced feature matches a feature already in the other language.

Compiling without PIAs

Visual Studio 2010 has first-class support for building both document- and application-level add-ins for the main Office applications such as Word and Excel. As part of automating these products, you will want to be able to call into the exposed COM interfaces. You do this by referencing the Primary Interop Assemblies (PIAs) in order to work with the Microsoft Office Object Model. In the past, this then introduced a deployment dependency requiring you to ensure that the PIAs not only existed but were also the version you required. This added unnecessary size and complexity to the deployment of your add-in.

Both VB and C# include support for deploying applications, whether they be add-ins or standalone applications that use Office automation, without relying on the users having the PIAs installed on their machine. In Figure 16-1 you can see that there is a new property that specifies whether the compiler should Embed Interop Types.

Figure 16-1

Figure 16-1. Figure 16-1

When the application is compiled, any interop types that are referenced are cloned from the PIAs into the compiled application. In Figure 16-2 you can see that the Microsoft.Office.Interop.Word.Application interface as been created within the CShaperLapAround executable.

When this application executes, the .NET Framework 4.0 uses a new feature called type equivalence to allow COM objects passed between managed assemblies to be cast to a corresponding type in the receiving assembly. This effectively means that two assemblies can both declare managed types that wrap a COM object and for them to be deemed type equivalent as if they both used the same type definition.

Figure 16-2

Figure 16-2. Figure 16-2

Note

Because PIA-less compilation relies on type equivalence, which is a feature of .NET Framework 4.0, the Embed Interop Types option is available only for projects that are compiling against the .NET Framework 4.0. With the ability for Visual Studio 2010 to target multiple versions of the framework, it is quite easy to accidentally create projects that are targeting an earlier version of the framework, in which case this option would not be available.

Generic Variance

One of the seemingly confusing aspects of Generics is the role, or lack thereof, of inheritance. Take, for example, the inheritance chain Tortoise, which inherits from Animal, which in turn inherits from Object. You would assume that if you have a List of Tortoise (that is, List<Tortoise> in C# or List(of Tortoise) in VB) that you could cast it back to a List of Animal. The following code illustrates why this cannot be allowed:

C#
private void InvalidGenericCast(){
    List<Tortoise> tortoiseList = new List<Tortoise>();
    List<Animal> animalList = tortoiseList;
    animalList.Add(new Lion());
    var notATortoise = tortoiseList[0];
}
Generic Variance
VB
Public Sub InvalidGenericCast()
    Dim tortoiseList As New List(Of Tortoise)
    Dim animalList As List(Of Animal) = tortoiseList
    animalList.Add(New Lion)
    Dim notATortoise As Tortoise = tortoiseList(0)
End Sub
Generic Variance

This code attempts to cast the List of Tortoise to a List of Animal. If this was allowed, a Lion could then be added to the list, because it too inherits from Animal. This would then make the last statement inconsistent because the List of Tortoise would no longer just contain Tortoises.

Though this illustrates a case against being able to cast between generic types, in some circumstances casting between types is allowable. For example, the following code snippet illustrates the List of Tortoise being cast to an IEnumerable of Animal. Because the IEnumerable interface doesn't permit modification of the collection, this is deemed to be a safe or allowable conversion.

C#
private void ValidGenericCast(){
    List<Tortoise> tortoiseList = new List<Tortoise>();
    tortoiseList.Add(new Tortoise());
    IEnumerable<Animal> animalList = tortoiseList;
    var firstAnimal = animalList.First();
}
Generic Variance
VB
Public Sub ValidGenericCast()
    Dim tortoiseList As New List(Of Tortoise)
    tortoiseList.Add(New Tortoise)
    Dim animalList As IEnumerable(Of Animal) = tortoiseList
    Dim firstAnimal As Animal = animalList.First()
End Sub
Generic Variance

The ability to convert between generic types in this way is referred to as generic variance. In some circumstances you want to be able to narrow the type variable, such as in the preceding example, and you want to be able to widen the type variable. These are known as covariance and contravariance.

Covariance

In the previous example you saw how IEnumerable of Tortoise can be cast to an IEnumerable of Animal. This is what is known as covariance and is allowable because the IEnumerable of T interface has been updated to include the out keyword:

C#
public interface IEnumerable<out T> : IEnumerable{
    IEnumerator<T> GetEnumerator();
}
VB
Interface IEnumerable(Of Out T) : Inherits IEnumerable
    Function GetEnumerator() As IEnumerator(Of T)
End Interface

Using the out keyword, you too can declare interfaces and delegates that have a variant type parameter. For example, in the following code the IAnimalCreator interface allows for the type parameter to be widened, allowing a conversion from IAnimalCreator of Lion (which MainForm implements) to IAnimalCreator of Animal, which the DoAnimalAction method expects:

C#
public interface IAnimalCreator<out T> where T:Animal{
    T CreateAnimal();
}

public partial class MainForm : Form, IAnimalCreator<Lion>{
    public MainForm(){
        InitializeComponent();
        var animal = DoAnimalAction(this);
        MessageBox.Show(animal.GetType().Name);
    }

    private Animal DoAnimalAction(IAnimalCreator<Animal> action) {
        return action.CreateAnimal();
    }

    Lion IAnimalCreator<Lion>.CreateAnimal(){
        return new Lion();
    }
}
Covariance
VB
Public Interface IAnimalCreator(Of Out T As Animal)
    Function CreateAnimal() As Animal
End Interface
Public Class MainForm
    Implements IAnimalCreator(Of Lion)

    Public Sub New()

        InitializeComponent()

        Dim animal = DoAnimalAction(Me)
        MessageBox.Show(animal.GetType().Name)

    End Sub

    Public Function DoAnimalAction(ByVal action As IAnimalCreator(Of Animal)) _
                                                                        As Animal
        Return action.CreateAnimal()
    End Function

    Public Function CreateAnimal() As Animal _
                                 Implements IAnimalCreator(Of Lion).CreateAnimal
        Return New Lion
    End Function
End Class
Covariance

You can see in this code that the conversion is safe because the CreateAnimal method doesn't accept any typed parameter (that is, a type parameter going in). Instead, the type parameter defines the type of the return, or out, value, making the interface covariant on T.

Contravariance

Cases exist where you also want to be able to widen the type parameter. This is known as contravariance and is used by the IComparer interface. As you can imagine, an IComparer of Animal is also an IComparer of Lion, because if you can compare any animal you should be able to compare Lions. This conversion is allowable because the IComparer interface has been updated to use the in keyword.

C#
public interface IComparer<in T>{
    public int Compare(T left, T right);
}
VB
Interface IComparer(Of In T)
    Function Compare(left As T, right As T) As Integer
End Interface

Again, you can use the in keyword to build your own contravariant interfaces or delegates. Using the Animal example again, you can define a method DoAnotherAnimalAction that will accept an IAnimalAction of Tortoise. However, the MainForm actually implements IAnimalAction of Animal.

C#
public interface IAnimalAction<in T> where T : Animal{
    void Action(T animal);
}

public partial class MainForm : Form, IAnimalAction<Animal>{
    public MainForm(){
        InitializeComponent();
        DoAnotherAnimalAction(this);
    }

    private void DoAnotherAnimalAction(IAnimalAction<Tortoise> action){
        action.Action(new Tortoise());
    }

    void IAnimalAction<Animal>.Action(Animal animal){
        MessageBox.Show("This could be any animal.... " + animal.GetType().Name);
    }
}
Contravariance
VB
Public Interface IAnimalAction(Of In T As Animal)
    Sub Action(ByVal animal As T)
End Interface

Public Class MainForm
    Implements IAnimalCreator(Of Lion), IAnimalAction(Of Animal)

    Public Sub New()
        InitializeComponent()
        DoAnotherAnimalAction(Me)
    End Sub

    Public Sub DoAnotherAnimalAction(ByVal action As IAnimalAction(Of Tortoise))
        action.Action(New Tortoise)
    End Sub

    Public Function CreateAnimal() As Animal _
                Implements IAnimalCreator(Of Lion).CreateAnimal
        Return New Lion
    End Function

    Public Sub Action(ByVal animal As Animal) _
                      Implements IAnimalAction(Of Animal).Action
        MessageBox.Show("This could be any animal.... " & animal.GetType().Name)
    End Sub
End Class
Contravariance

Looking at this example, you may be wondering why it is safe to perform the conversion between an IAnimalAction of Animal to an IAnimalAction of Tortoise. This operation is safe because the compiler enforces that the contravariant type parameter, T, can only be used as an input parameter. Because a Tortoise can always be converted to an Animal, it is always safe to use a Tortoise as an input parameter for a method that expects an Animal.

VISUAL BASIC

This release of Visual Basic (VB) includes a number of additions that bring it closer to feature parity with C#. It also includes a couple of language-specific features that make it easier for developers to initialize collections and arrays.

Lambdas and Anonymous Methods

One of the key omissions from previous versions of VB was full support for lambdas and anonymous methods. An anonymous method is a method that is defined without a name and a lambda is a special case whereby the expression can be either used to generate a delegate (as with most anonymous methods) or an expression tree (discussed further in Chapter 29 on LINQ). In VB you now have the ability to declare single and multiline anonymous methods.

The following code snippet illustrates a number of features of working with anonymous methods in VB. In the first line an anonymous function is declared that accepts a name parameter and returns a Boolean. Type inference is used to determine that the name parameter should be a string and in fact in this case the As Boolean can be omitted because the return type can also be inferred. If the inferred input type is wrong or you wish to make your code more readable you can also specify the type of the input parameter.

VB
Dim exp = Function(name) As Boolean
              Console.WriteLine("Hello " & name)
              Return name.length > 10
          End Function
Dim exp2 = Sub(name)
               If exp(name) Then
                   Console.WriteLine(name & " is longer than 10 characters")
               End If
           End Sub
exp("Fred")
Dim names = {"Fred", "Joe", "Sandra"}
Array.ForEach(names, exp2)
Array.ForEach(names, Sub(name As String) Console.WriteLine(name))
Lambdas and Anonymous Methods

The second line illustrates that you can create an anonymous method with no return type, also know as a Sub in VB. Both lines return a delegate that can be invoked by supplying a parameter, illustrated in the next two lines. The final line illustrates how you can define an anonymous method in line as part of a method call. Note here that the abbreviated form has been used, as the Sub is a single line. If your method has multiple lines, you need to use the full notation that includes an End Sub or End Function, depending on whether it has a return value.

Implicit Line Continuation

Where possible, the VB compiler will infer line continuation. For example, you can now write the following with no line continuation characters:

VB
Public Function LongMethodDeclaration(ByVal parameterOne As Integer,
                                      ByVal parameterTwo As Integer,
                                      ByVal parameterThree As Integer,
                                      ByVal parameterFour As Integer,
                                      ByVal parameterFive As Integer,
                                      ByVal parameterSix As Integer) As Integer
    Return parameterOne + parameterTwo + parameterThree +
                parameterFour + parameterFive + parameterSix
End Function
Implicit Line Continuation

In some cases, a line continuation character is still required. For example, if you wanted the parameter list to start on a new line:

VB
Public Function LongMethodDeclaration _
                                     (ByVal parameterOne As Integer,
                                      ByVal parameterTwo As Integer,

Implicit line continuation makes writing LINQ expressions with VB much easier, as you can break up the expression over multiple lines without having to add the line break character each time.

Automatic Properties with Initial Values

When defining a class, it is good practice to use encapsulation to hide or encapsulate the functionality of your class. The idea is that if you need to change the implementation, you can do so without affecting other code that uses that class. As such, it is recommended that where you want to expose a field, it should be done via a property. The property can simply be a getter/setter, or it can contain additional functionality, for example, that raises an event when the property value changes. This practice has resulted in large amounts of repetitive code where a property is declared along with a backing field. Although this has been made easier with Visual Studio snippets, it still results in code that is overly verbose.

In Visual Studio 2010, VB now not only has automatic properties — properties that automatically implement the backing field — it also enables you to declare an initial value for the property in the same way as you would for a field. The initial value is set by calling the property setter after the object instance is initialized but prior to any constructor being invoked. The following code illustrates this with the MaximumWordCount property:

VB
Public Property MaximumWordCount As Integer = 10
Public Sub New()
    MessageBox.Show("The maximum word count is " & MaximumWordCount)
    MessageBox.Show("The maximum word count is " & _MaximumWordCount)
Automatic Properties with Initial Values

Nowhere in this code snippet is the _MaximumWordCount field declared. VB exposes the backing field used by the automatic property by simply prepending the property name with an underscore. Although it is not recommended, you can access the field directly from within your class because it is declared with a scope of Private.

Collection Initializers and Array Literals

VB now has a compact notation for specifying arrays, illustrated in the following code snippet:

VB
' Single dimension arrays
Dim a = {56, 34, 29, 12, 35, 872, 34, 12, 66} 'Integer()
Dim b = {1, 5, 3.54, 3.5} 'Double()
Dim c = {"Betty", "Frank"} 'String()
Dim d = {1, "123", New Animal} 'Object()

'Multi-dimension arrays
Dim e = {{1, 2, 3},
         {4, 5, 6}} 'non-jagged array (ie Integer(,) )
Dim f = {({1, 2, 3}),
         ({4}),
         ({5, 6, 7})} 'jagged array (ie Integer()())
Collection Initializers and Array Literals

Note that the type of the array is inferred from the type of each of the constituent values. As such, the array b is an array of Double because this allows both the first two values, which are Integers, and the remaining values to be inserted. The Object array will raise an error if Option Strict is on.

Lists and Dictionaries can also be initialized using a similar compact notation:

VB
Private listOfNames As New List(Of String) From {"Nick", "Dave", "Mike", "Chris"}
Private cityLookup As New Dictionary(Of String, String) _
                                 From {{"Nick", "Sydney"},
                                       {"Dave", "Perth"},
                                       {"Mike", "Perth"},
                                       {"Chris", "Sydney"}}
Collection Initializers and Array Literals

The initialization of the List and Dictionary is done by invoking the Add method on the newly created instance. You can use a similar compact syntax to add your own object types to a List by creating an extension method, named Add, that will convert a set of input values into an instance of your class.

VB
Private listOfWeirdObjects As New List(Of MyListClass) From {{"Boo", 45, 67, 4.5},
                                                             {"Foo", 29, 34, 7.4}}
Public Module Extensions
    <Extension()>
    Sub Add(ByVal list As List(Of MyListClass),
            ByVal Name As String,
            ByVal Height As Integer,
            ByVal Weight As Integer,
            ByVal Width As Double)

        list.Add(New MyListClass With {
                                        .Name = Name,
                                        .Height = Height,
                                        .Weight = Weight,
                                        .Width = Width
                                   })
    End Sub

End Module
Collection Initializers and Array Literals

Nullable Optional Parameters

In the past it was not possible for nullable parameters to be optional. Now they can be, allowing you to define methods such as the following:

VB
Public Sub New()
    MethodWithOptionalParameters(5, parameterThree:=6)
End Sub
Public Function MethodWithOptionalParameters _
                       (Optional ByVal parameterOne As Integer? = Nothing,
                        Optional ByVal parameterTwo As Integer? = 0,
                        Optional ByVal parameterThree As Integer? = Nothing) _
                                                                   As Integer
Nullable Optional Parameters

As you can see from the code, for the optional parameters you need to define a default value. Also, when calling the method you may need to use parameter naming (as in parameterThree:=) if you wish to skip an optional parameter.

Visual Basic PowerPacks

One of the challenges often put forward by VB6 developers is that doing tasks in .NET requires many more steps or is more complex than it was in VB6. To encourage VB6 developers across to the .NET Framework, VB introduced the My namespace, which provides a set of shortcut methods to get frequently performed tasks done. The VB team has also released the Visual Basic PowerPacks for previous versions of Visual Studio that add a number of useful controls and other classes to aid VB developers.

Visual Studio 2010 ships with the Visual Basic PowerPacks. As you can see in Figure 16-3, an additional tab in the Toolbox contains a number of drawing controls such as Line, Oval, and Rectangle. These can be used to generate simple graphics, such as the one on the right-hand side of Figure 16-3.

Figure 16-3

Figure 16-3. Figure 16-3

Although the Visual Basic PowerPacks are available by default to VB developers, there is no reason why C# developers can't access the same controls. To use these controls in a C# project, simply add a reference to the PowerPacks assembly and then add the controls to your Toolbox. From there you can use them on any Windows Forms application.

C#

In this iteration of the C# language there are only a couple of new features that mainly focus around the ability to interop with both native and dynamic languages.

Late Binding with Dynamic Lookup

Interoperability with other languages/technologies can often be quite painful, particularly with dynamic languages where it is not always known up front what methods a class may contain. In the past it was possible to execute these calls, but it often required a fairly in-depth understanding of reflection, and even then required many calls to invoke a single method. The new dynamic keyword can be used to allow methods to be late bound:

C#
public class DynamicClass{}
public class MoreDynamic : DynamicClass{
public void SimpleMethod(){
        MessageBox.Show("Dynamic Invoked");
    }
}

public MainForm(){
    InitializeComponent();

    dynamic lateBound = CreateDynamic();
    lateBound.SimpleMethod();
}

public object CreateDynamic(){
    return new MoreDynamic();
}
Late Binding with Dynamic Lookup

In this code example the CreateDynamic method returns a MoreDynamic object as just an object. By declaring the lateBound object using the dynamic keyword it is possible to invoke the SimpleMethod the same way as if the method was declared on the object. Without this keyword, the static type checker would be invoked and a compile error would be thrown because the SimpleMethod is not declared on object (which is returned from CreateDynamic and thus would be the inferred type of the lateBound variable).

Note

One of the challenges with using the dynamic keyword is that the onus is now firmly on you to make sure you get the method naming, type, and number of parameters correct to ensure your code doesn't fail at run time. The dynamic keyword effectively blocks the static type checking at compile time, deferring it to the point where the code is executed.

Named and Optional Parameters

Two features that have been noticeably absent from C# are the ability to define optional parameters and to specify parameters using their names. Both these features contribute to making code more usable and more readable because they do away with unnecessary bloat (that is, specifying null for parameters you don't need/want to specify or having additional method overloads to effectively define optional parameters) and allow parameter values to be named.

C#
public MainForm(){
    InitializeComponent();
    var output = MethodWithOptionalParameters(parameterTwo: 15);
}
public int MethodWithOptionalParameters(int? parameterOne = null,
                                        int parameterTwo = 5){
    return (parameterOne ?? 0) + parameterTwo;
}
Named and Optional Parameters

In this code both parameters are optional: the first being a nullable int with a default value of null, the second being a normal int with a default value of 5. Parameters become optional when a default value is specified as part of the method signature. When calling a method with optional parameters, those parameters can simply be omitted. Regardless of whether parameters are optional, you can name each of the parameter values so that someone reading your code can easily understand what the parameter values correspond to. This is particularly important if you are supplying a constant value, as is the case in the preceding code snippet, where the meaning of the constant value is not immediately obvious.

The use of named and optional parameters is particularly useful when working with COM interfaces. These quite often have a number of optional parameters that in the past would have had to be specified. This is no longer the case because those parameters can simply be omitted. Using named parameter values makes it clear which parameters you are supplying.

Note

Calling methods using named parameters can make your code more brittle to changes. For example, say you access a third-party control that has a method with a single parameter called height. If the control vendor does a version update and changes the parameter name to controlHeight, even if the method signature didn't change, your code will no longer be able to locate the height parameter, so it will fail.

F#

F# (pronounced F Sharp) is a relatively new language incubated out of Microsoft Research in Cambridge, England, by the guy that brought generics to the .NET Framework, Don Syme. Microsoft's Developer Division recently welcomed F# to the Visual Studio range of supported languages and it ships in the box with Visual Studio 2010. F# is a multi-paradigm functional language. This means it's primarily a functional language, but supports other flavors of programming, such as imperative and object-oriented programming styles.

Your First F# Program

Fire up Visual Studio 2010 and create a new F# project. As Figure 16-4 shows, the F# Application template is located in the Visual F# node in the New Project dialog. Give it a name and click OK.

Figure 16-4

Figure 16-4. Figure 16-4

The F# Application template simply creates an F# project with a single source file, Program.fs, which is empty except for a reference to the F# Developer Center, http://fsharp.net. If you want to learn more about F# a great place to start is the F# Tutorial template. This creates a normal F# project except for the main source file, Tutorial.fs, which contains approximately 280 lines of documentation on how to get started with F#. Walking down this file and checking out what language features are available is an interesting exercise in itself. For now, return to the Program.fs and quickly get the canonical "Hello World" example up and running to see the various options available for compilation and interactivity. Add the following code:

#light

printfn "Hello, F# World!"

The first statement, #light, is a compile flag to indicate that the code is written using the optional lightweight syntax. With this syntax, whitespace indentation becomes significant, reducing the need for certain tokens such as in and ;;. The second statement simply prints out "Hello, F# World!" to the console.

Note

If you have worked with previous versions of F# you may find that your code now throws compiler errors. F# was born out of a research project and it has only now been converted into a commercial offering. As such, there has been a refactoring of the language and some operations have been moved out of FSharp.Core into supporting assemblies. For example, the print_endline command has been moved into the FSharp.PowerPack.dll assembly. The F# Powerpack is available for download via the F# Developer Center at http://fsharp.net.

You can run an F# program in two ways. The first is to simply run the application as you would normally (press F5 to start debugging). This compiles and runs your program as shown in Figure 16-5.

Figure 16-5

Figure 16-5. Figure 16-5

The other way to run an F# program is to use the F# Interactive window from within Visual Studio. This allows you to highlight and execute code from within Visual Studio, and immediately see the result in your running program. It also allows you to modify your running program on the fly!

Figure 16-6

Figure 16-6. Figure 16-6

The F# Interactive window is available from the View

Figure 16-6

In the Interactive window, you can start interacting with the F# compiler through the REPL (Read Eval Print Loop) prompt. This means that for every line of F# you type, it will compile and execute that line immediately. REPLs are great if you want to test ideas quickly and modify programs on the fly. They allow for quick algorithm experimentation and rapid prototyping.

However, from the REPL prompt in the F# Interactive window, you essentially miss out on the value that Visual Studio delivers through IntelliSense, code snippets, and so on. The best experience is that of both worlds: using the Visual Studio text editor to create your programs, and piping that output through to the Interactive Prompt. You can do this by hitting Alt+Enter on any highlighted piece of F# source code. Alternatively, you can use the right-click context menu to send a selection to the Interative window, as shown in Figure 16-7.

Figure 16-7

Figure 16-7. Figure 16-7

Pressing Alt+Enter, or selecting Send To Interactive, pipes the highlighted source code straight to the Interactive Prompt and executes it immediately, as shown in Figure 16-8.

Figure 16-8

Figure 16-8. Figure 16-8

Figure 16-8 also shows the right-click context menu for the F# Interactive window where you can either Cancel Evaluation (for long running operations) or Reset Session (where any prior state will be discarded).

Exploring F# Language Features

A primer on the F# language is beyond the scope of this book, but it's worth exploring some of the cooler language features that it supports. If anything, it should whet your appetite for F#, and act as a catalyst to learn more about this great language.

A very common data type in the F# world is the list. It's a simple collection type with expressive operators. You can define empty lists, multi-dimensional lists, and your classic flat list. The F# list is immutable, meaning you can't modify it once it's created; you can only take a copy. F# exposes a feature called List Comprehensions to make creating, manipulating, and comprehending lists easier and more expressive. Consider the following:

#light

let countInFives = [ for x in 1 .. 20 do if x % 5 = 0  then yield x ]

printf "%A" countInFives
System.Console.ReadLine()

The expression in braces does a classic "for" loop over a list that contains elements 1 through 20 (the ".." expression is shorthand for creating a new list with elements 1 through 20 in it). The "do" is a comprehension that the "for" loop executes for each element in the list. In this case, the action to execute is to "yield" x where the if condition "when x module 5 equals 0" is true. The braces are shorthand for "create a new list with all returned elements in it." And there you have it — a very expressive way of defining a new list on the fly in one line.

F#'s Pattern Matching feature is a flexible and powerful way to create control flow. In the C# world, we have the switch (or simply a bunch of nested "if else's"), but we're usually constrained to the type of what we're switching over. F#'s pattern matching is similar, but more flexible, allowing the test to be over whatever types or values you specify. For example, take a look at defining a Fibonacci function in F# using pattern matching:

let rec fibonacci x =
    match x with
    | 0 | 1 -> x
    | _ -> fibonacci (x - 1) + fibonacci (x - 2)

printfn "fibonacci 15 = %i" (fibonacci 15)

The pipe operator (|) specifies that you want to match the input to the function against an expression on the right side of the pipe. The first says return the input of the function x when x matches either 0 or 1. The second line says return the recursive result of a call to Fibonacci with an input of x – 1, adding that to another recursive call where the input is x – 2. The last line writes the result of the Fibonacci function to the console.

Pattern matching in functions has an interesting side effect — it makes dispatch and control flow over different receiving parameter types much easier and cleaner. In the C#/VB.NET world, you would traditionally write a series of overloads based on parameter types, but in F# this is unnecessary, because the pattern matching syntax allows you to achieve the same thing within a single function.

Lazy evaluation is another neat language feature common to functional languages that F# also exposes. It simply means that the compiler can schedule the evaluation of a function or an expression only when it's needed, rather than precomputing it up front. This means that you only have to run code you absolutely have to — fewer cycles spent executing and less working set means more speed.

Typically, when you have an expression assigned to a variable, that expression gets immediately executed in order to store the result in the variable. Leveraging the theory that functional programming has no side effects, there is no need to immediately express this result (because in-order execution is not necessary), and as a result, you should only execute when the variable result is actually required. Take a look at a simple case:

let lazyDiv = lazy ( 10 / 2 )
printfn "%A" lazyDiv

First, the lazy keyword is used to express a function or expression that will only be executed when forced. The second line prints whatever is in lazyDiv to the console. If you execute this example, what you actually get as the console output is "(unevaluated)." This is because under the hood the input to printfn is similar to a delegate. You actually need to force, or invoke, the expression before you'll get a return result, as in the following example:

let lazyDiv2 = lazy ( 10 / 2 )
let result = lazyDiv2.Force()
print_any result

The lazyDiv2.Force() function forces the execution of the lazyDiv2 expression.

This concept is very powerful when optimizing for application performance. Reducing the amount of working set, or memory, that an application needs is extremely important in improving both startup performance and run time performance. Lazy evaluation is also a required concept when dealing with massive amounts of data. If you need to iterate through terabytes of data stored on disk, you can easily write a Lazy evaluation wrapper over that data, so that you only slurp up the data when you actually need it. The Applied Games Group in Microsoft Research has a great write-up of using F#'s Lazy evaluation feature with exactly that scenario: http://blogs.technet.com/apg/archive/2006/11/04/dealing-with-terabytes-with-f.aspx.

SUMMARY

In this chapter you learned about the different styles of programming languages and about their relative strengths and weaknesses. Visual Studio 2010 brings together the two primary .NET languages, C# and VB, with the goal of reaching feature parity. The co-evolution of these languages will help reduce the cost of development teams and projects, allowing developers to more easily switch between languages. You also learned about the newest addition to the supported Microsoft languages, Visual F#. As the scale of problems that we seek to solve increases, so does the complexity introduced by the need to write highly parallel applications. Visual F# can be used to tackle these problems through the execution of parallel operations without adding to the complexity of an application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.160.181