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
a Tale of Two languages .
315
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
316 .
chaPter 16 lAnguAge-SpeciFic FeATureS
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
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];
}
Code snippet MainForm.cs
a Tale of Two languages .
317
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
Code snippet MainForm.vb
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();
}
Code snippet MainForm.cs
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
Code snippet MainForm.vb
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.
318 .
chaPter 16 lAnguAge-SpeciFic FeATureS
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();
}
}
Code snippet MainForm.cs
Vb
Public Interface IAnimalCreator(Of Out T As Animal)
Function CreateAnimal() As Animal
End Interface
a Tale of Two languages .
319
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
Code snippet MainForm.vb
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.
320 .
chaPter 16 lAnguAge-SpeciFic FeATureS
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);
}
}
Code snippet MainForm.cs
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
Code snippet MainForm.vb
Visual Basic .
321
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