I also said it would be nice if ReSharper could separate the assignments onto separate lines… and got reply from Jenya Legkiy who works at JetBrains who said it already does.
So to make up for it I’ve done my first screencast to show how you can you use ReSharper to refactor your code to use an object initializer and then go back to having the assignments on separate lines.
But as I’ll demonstrate in this post you can have too much of a good thing.
When object initializers go bad
The code below shows an example of mapping a model object from a data reader with and without using an object initializer.
using System;
using System.Data;
public class Mapper
{
public static Model Map(IDataReader reader)
{
if (reader.Read())
{
Model model = new Model();
model.FirstName = reader[0].ToString();
model.LastName = reader[1].ToString();
model.Age = Convert.ToInt32(reader[2].ToString());
return model;
}
return null;
}
public static Model MapUsingObjectInitializer(IDataReader reader)
{
if (reader.Read())
{
return new Model
{
FirstName = reader[0].ToString(),
LastName = reader[1].ToString(),
Age = Convert.ToInt32(reader[2].ToString())
};
}
return null;
}
}
public class Model
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
If we run two unit tests to map the model from a data reader we will get two passing tests.
using System.Data;
using NUnit.Framework;
[TestFixture]
public class When_Mapping_Model_Using_DataReader
{
[Test]
public void Should_Be_Able_To_Map_Model()
{
Mapper.Map(GetDataReader());
}
[Test]
public void Should_Be_Able_To_Map_Model_Using_Object_Initializer()
{
Mapper.MapUsingObjectInitializer(GetDataReader());
}
public static IDataReader GetDataReader()
{
DataTable table = new DataTable();
DataRow row = table.NewRow();
table.Columns.Add(new DataColumn("FirstName"));
table.Columns.Add(new DataColumn("LastName"));
table.Columns.Add(new DataColumn("Age"));
row["FirstName"] = "Bob";
row["LastName"] = "Smith";
row["Age"] = "55";
table.Rows.Add(row);
return new DataTableReader(table);
}
}
Now if we modified the GetDataReader method to return a string value for age
We get a failing test when mapping without an object initializer as shown below
As you can see from the output message above we can easily find the line of code (line 13) that caused the exception.
When a test fails for the code using an object initializer
the line of code that caused the exception (line 23) is the same one in which the object was created.
The fact is the more properties you initialize using an object initializer the harder it will be to find the one that causes an exception.
What’s ReSharper got to do with this?
In a word temptation. It makes it so easy to convert your code to use one.
That’s not ReSharpers fault
I agree. Ultimately it’s a developers choice if they want to use the refactoring that tools like ReSharper suggest, after all just because you can doesn’t mean you should.
However it could give you an option not to convert to object initializer if more than say ten properties were going to be initialized.
Or detect if the object initializer wasn’t a straight forward mapping and then not suggest using one like in this example.
What ReSharper is missing
What would be really useful if you could convert code that was using an object initializer to the classic way of setting properties one line at a time.
For a while now I thought there was a bug when using Resharper to run my unit tests.
Because my latest changes were not being picked up, I always had to build my solution first and then run the unit tests.
Not anymore.
Today a colleague showed me this ‘Build Settings’ option
Selecting ‘Always Build’ makes sure the latest changes (even to config files) are used when running unit tests. The default is ‘Automatic’ as shown above.
Just be aware of the size of your solution will have an impact on how long the tests will take to run.