Can I enforce that fields in a POCO are only ever set in a type initialiser?

If I have a POCO such as this:

[Poco]
public class MyPoco
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
}

... and it's initialised like this:

new MyPoco { FirstName = "Joe", LastName="Bloggs", Address="123 Abc St." }

Can I, using CQL in NDepend, ensure that the properties are only ever set using the object initialiser as above, and not separately.

I'm trying to catch code like this:

myPoco.FirstName="John";
doSomething(myPoco);

... I don't want people to set properties on an existing POCO. I understand I can make the setters private, but I don't like passing in all the properties in a constructor because you end up with a signature such as MyPoco(string, string, string) - making it easier to misalign the parameters (easier than using the object initialiser syntax)

So, I want to catch this using NDepend. Here's the start of my CQL query:

from f in JustMyCode.Fields.Where(f=>
   f.ParentType.HasAttribute ("JetBrains.Annotations.PocoAttribute".AllowNoMatch())) 
       where f.MethodsAssigningMe [I'M STUMPED HERE]

It looks like I can analyse the method setting the property of a POCO, but I need to go one step lower and examing the code block to see if it's in an object initialiser block.

Is this possible?

Jon Skeet
people
quotationmark

I don't know whether you can do this in CQL, although even if you can't, you might well be able to do it with Roslyn.

However, you might want to consider using a builder pattern instead:

var poco = new MyPoco.Builder { FirstName = "Joe",
                                LastName="Bloggs",
                                Address="123 Abc St." }.Build();

Now the poco itself can be immutable, but the builder can have the properties set wherever you like.

Heck, if you're a fan of implicit conversions you could even have an implicit conversion from the builder to the poco:

MyPoco poco = new MyPoco.Builder { FirstName = "Joe",
                                   LastName="Bloggs",
                                   Address="123 Abc St." };

While I'm all for tools verifying proper coding where it's not possible to design a type to avoid it being misused to start with, I'd prefer to have an idiot-proof API to start with :)

Also note that with named arguments in C# 4, even the constructor version can be made clear:

var poco = new MyPoco(firstName: "Joe",
                      lastName: "Bloggs",
                      address: "123 Abc St.");

but of course that still allows people to write it without the named arguments...

people

See more on this question at Stackoverflow