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?
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...
See more on this question at Stackoverflow