RSS 2.0
Sign In
# Monday, 24 February 2014

While writing an article Dealing with dynamic SQL in SQL Server we have crushed on a trivial C# code.

 Consider a declaration:

public enum Direction
{
  Asc,
  Desc
}

public struct Order
{
  public string Field { get; set; }
  public Direction Direction { get; set; }
}

public class DateRange
{
  public DateTime? From { get; set; }
  public DateTime? To { get; set; }
}

public class Request
{
  public DateRange CreatedAt { get; set; }
  public string Summary { get; set; }
  [XmlElement]
  public int[] State { get; set; }
  public DateRange UpdatedAt { get; set; }
  [XmlElement]
  public Order[] Order { get; set; }
}

and a code:

  ...
  var request = new Request
  {
    CreatedAt = { From = new DateTime(2014, 1, 1) },
    Order = new[]
    {
      new Order { Field = "CreatedAt" }
    }
  };
  ...

It was hard to believe that this simple variable declaration throws NullReferenceException.

In fact we could not realize what the reason is until we have decompiled that code and poked into IL.

It appears that the problem is with the line:

CreatedAt = { From = new DateTime(2014, 1, 1) },

It's implemented like this:

var range = request.CreatedAt;

range.From = new DateTime(2014, 1, 1) ;
range.To = null;

In other words it assumes the instance stored in property CreatedAt is not null, and it reinitializes all its properties.

In contrast the following works as expected (no exception is thrown):

CreatedAt = new { From = new DateTime(2014, 1, 1) },

as it creates a new instance and assigns it to a property.

We think that this is very error prone, so if it's how C# is assumed to be then it's the problem of the spec, otherwise it's the problem of Microsoft implementation of C# (at least in VS 2010 - 2013).

Monday, 24 February 2014 08:48:35 UTC  #    Comments [4] -

Monday, 24 February 2014 17:25:35 UTC
It's clearly behavior as described in the spec unless I'm missing something. (7.6.10.2 of CLS.) Object initializers run after the constructor and nothing is assumed besides that. They are compiled as a series of assignments through a temp variable to prevent partially initialized objects, exactly like what you see in the decompile. One needs to imagine the object initializer syntax as a shorthand for these assignments and nothing more, then it's hopefully less surprising.

C# in Depth by Jon Skeet has it pretty clearly.
Yuri
Monday, 24 February 2014 21:34:06 UTC
Hello, Yuri!

You have found that this is the spec.

In our opinion that's the worse resolution, as we doubt many people would immediately spot the problem looking at those declarations.

Our point is that a language construct for a declaration like:

var request = new Request
{
CreatedAt = { From = new DateTime(2014, 1, 1) }
};

should not result in an error, as it's counter intuitive, and thus error prone.

In our opinion it would be better for this declaration to work as
CreatedAt = new { From = new DateTime(2014, 1, 1) },

or to be forbidden.
Vladimir Nesterovsky
Tuesday, 25 February 2014 07:08:14 UTC
I see your point... but I can also look at this from the standpoint of C# language designers. They clearly didn't want to change the semantics of object creation and initialization, that is default values, constructor semantics etc. I think that was the right choice. Given that, everything else flows logically from there - the object initializers are assignments run after the constructor has finished, and the object initializers' semantics is completely orthogonal to the constructor semantics. Therefore, the language has no idea whether the properties have or have not been initialized, and either choice you offer can be restrictive under certain circumstances.

I agree the syntax looks odd and counterintuitive. But the object initializers were added to the language not as nice syntactic sugar but rather as a critical feature that enables instantiation and full initialization of anonymous classes in a single expression. It's understandable it required a compromise. Seen much worse in Java :-) Seriously, if you find this awkward the best would be to avoid this construct in your code and have fully parameterized constructors instead (unless you need it for this exact reason as mentioned above.) With named parameters you will get almost the same syntax as object initializers (round parenthesis instead of curly braces.) I think this is much better from any perspective, especially if you want to have immutable objects. The object initializers have a very specific goal, outside of the anonymous class instantiation usage they aren't perfect, I agree.
Yuri
Tuesday, 25 February 2014 09:03:39 UTC
Object initializers are good, and we use it extensively.

But there are two sides here.
I'll use spec examples here:

1. initializers that use new operator.

public class Point
{
int x, y;
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}

public class Rectangle
{
Point p1, p2;
public Point P1 { get { return p1; } set { p1 = value; } }
public Point P2 { get { return p2; } set { p2 = value; } }
}

Rectangle r = new Rectangle {
P1 = new Point { X = 0, Y = 1 },
P2 = new Point { X = 2, Y = 3 }
};

2. nested object initializer

public class Rectangle
{
Point p1 = new Point();
Point p2 = new Point();
public Point P1 { get { return p1; } }
public Point P2 { get { return p2; } }
}

Rectangle r = new Rectangle {
P1 = { X = 0, Y = 1 },
P2 = { X = 2, Y = 3 }
};


#1 works always, #2 assumes members are not null.

Everything is good except that we don't like to have no single warning at compile time and have NRE at runtime for a trivial initialization.

We would prefer "nested object initializer" to be defined differently:

a) if property has a setter then it should create a new instance and assign it to the property.
b) if there is only a getter then it should ask the property value, and assign sub properties.
Vladimir Nesterovsky
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

[Captcha]Enter the code shown (prevents robots):

Live Comment Preview
Archive
<2014 February>
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
2324252627281
2345678
Statistics
Total Posts: 387
This Year: 3
This Month: 0
This Week: 0
Comments: 2178
Locations of visitors to this page
Disclaimer
The opinions expressed herein are our own personal opinions and do not represent our employer's view in anyway.

© 2024, Nesterovsky bros
All Content © 2024, Nesterovsky bros
DasBlog theme 'Business' created by Christoph De Baene (delarou)