Mark Needham

Thoughts on Software Development

Archive for the ‘linq’ tag

C#: A lack of covariance with generics example

with 10 comments

One of the things I find most confusing when reading about programming languages is the idea of covariance and contravariance and while I’ve previously read that covariance is not possible when using generics in C# I recently came across an example where I saw that this was true.

I came across this problem while looking at how to refactor some code which has been written in an imperative style:

public interface IFoo 
{
	string Bar { get; set; } 
}
 
public class Foo : IFoo 
{ 
	public string Bar { get; set; }
}
private IEnumerable<IFoo> GetMeFoos()
{
    var someStrings = new[] { "mike", "mark" };
 
    var someFoos = new List<IFoo>();
    foreach (var s in someStrings)
    {
        someFoos.Add(new Foo { Bar = s });
    }
    return someFoos;
}

I changed the code to read like so:

private IEnumerable<IFoo> GetMeFoos()
{
    var someStrings = new[] { "mike", "mark" };
    return someStrings.Select(s => new Foo { Bar = s });
}

Which fails with the following compilation error:

Error	1	Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Test.Foo>' to 'System.Collections.Generic.IEnumerable<Test.IFoo>'. An explicit conversion exists (are you missing a cast?)

I thought the compiler would infer that I actually wanted a collection of ‘IFoo’ given that I was returning from the method directly after the call to Select but it doesn’t.

As I understand it the reason that we can’t downcast an IEnumerable of ‘Foo’ to an IEnumberable of ‘IFoo’ is that we would run into problems if we worked of the assumption that our original collection only contained Foos in it later on in our program.

For example it would be possible to add any item which implemented the ‘IFoo’ interface into the collection even if it wasn’t a ‘Foo':

// this code won't compile
 
List<Foo> foos = new List<Foo>();
// add some foos
 
List<IFoo> ifoos = foos;
 
foos.Add(new SomeOtherTypeThatImplementsIFoo());

It’s not possible to convert ‘SomeOtherTypeThatImplementsIFoo’ to ‘Foo’ so we would run ourself into problems.

Rick Byers has a post from a few years ago where he explains how this works in more detail and also points out that covariance of generics is actually supported by the CLR, just not by C#.

In the case I described we can get around the problem by casting ‘Foo’ to ‘IFoo’ inside the ‘Select':

private IEnumerable<IFoo> GetMeFoos()
{
    var someStrings = new[] { "mike", "mark" };
    return someStrings.Select(s => (IFoo) new Foo { Bar = s });
}

Written by Mark Needham

February 20th, 2010 at 12:17 pm

Posted in .NET

Tagged with ,

Getting a strongly typed collection using LINQ to Xml

without comments

I mentioned earlier that I have been playing around with LINQ to Xml for parsing a Visual Studio csproj file.

While having namespace issues I decided to try and parse a simpler Xml file to try and work out what I was doing wrong.

Given this fragment of Xml:

<Node>
  <InnerNode>mark</InnerNode>
  <InnerNode>needham</InnerNode>
</Node>

I wanted to get a collection(IEnumerable) of InnerNode values.

Unfortunately my over enthusiasm to use anonymous types meant that I caused myself more problems than I needed to. This was my original (failed) effort at doing so:

1
2
3
4
5
6
7
8
var innerNodes = from node in projectFile.Descendants("Node").Elements()
                 select new {InnerNode = node.Value};
 
IList<string> innerNodesAsCollection = new List<string>();
foreach (var innerNode in innerNodes)
{
    innerNodesAsCollection.Add(innerNode.InnerNode);
}

A very round about way of solving the problem I’m sure you’ll agree. I was sure this should be easy to do but I was making it very complicated indeed. A bit more googling revealed that I could put items straight into the collection from the LINQ query by not using anonymous types:

1
2
IEnumerable<string> innerNodes = from node in projectFile.Descendants("Node").Elements()
                                 select node.Value;

Much less code, much simpler, and a lesson in the art of not over complicating things.

Written by Mark Needham

August 30th, 2008 at 3:03 am

Posted in .NET

Tagged with , ,

Querying Xml with LINQ – Don’t forget the namespace

with one comment

I’ve been working with a colleague on parsing a Visual Studio project file using LINQ to effectively create a DOM of the file.

The first thing we tried to do was get a list of all the references from the file. It seemed like a fairly easy problem to solve but for some reason nothing was getting returned:

1
2
3
4
XDocument projectFile = XDocument.Load(projectFilePath.Path);
 
var references = from itemGroupElement in projectFile.Descendants("ItemGroup").First().Elements()
                 select itemGroupElement.Attribute("Include").Value;

We are selecting all the occurrences of ‘ItemGroup’, taking the first occurrence, getting all the elements inside it (i.e. all the Reference elements) and then selecting the value of the ‘Include’ attribute. A fragment of the csproj file is as follows:

1
2
3
4
5
6
7
<ItemGroup>
	<Reference Include="System" />
	<Reference Include="System.Core">
		<RequiredTargetFramework>3.5</RequiredTargetFramework>
	 </Reference>
...
</ItemGroup>

After several hours of trial and error it turned out that we just needed to include the namespace of the file when querying. The new and now working code looks like this:

1
2
3
4
5
XNamespace projectFileNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
XDocument projectFile = XDocument.Load(projectFilePath.Path);
 
var references = from itemGroupElement in projectFile.Descendants(projectFileNamespace + "ItemGroup").First().Elements()
                 select itemGroupElement.Attribute("Include").Value;

There are two quite clever things going on with the way this is done

1) There is an implicit type conversion defined on XNamespace which allows us instatiate it using a string.
2) The addition(+) operator has been overloaded on XNamespace so that it can combine the namespace with the local name (‘ItemGroup’). This is described in more detail here.

Written by Mark Needham

August 28th, 2008 at 10:15 am

Posted in .NET

Tagged with , , , ,