我是否可以强制执行POCO中的字段只能在类型初始化器中设置?

时间:2014-06-20 08:24:15

标签: c# poco code-analysis cql ndepend

如果我有这样的POCO:

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

......它的初始化如下:

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

我可以在NDepend中使用CQL,确保只使用上面的对象初始化程序设置属性,而不是单独设置。

我正试图抓住这样的代码:

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

...我不希望别人在现有POCO 上设置属性。我知道我可以制作setter private,但我不喜欢传递构造函数中的所有属性,因为你最终会得到MyPoco(string, string, string)之类的签名 - 这样会更容易错位参数(比使用对象初始化语法更容易

所以,我想用NDepend来捕获它。这是我的CQL查询的开始:

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

看起来我可以分析设置POCO属性的方法,但我需要更低一步并检查代码块以查看它是否在对象初始化块中。

这可能吗?

1 个答案:

答案 0 :(得分:5)

我不知道您是否可以在CQL中执行此操作,但即使您不能,也可以使用Roslyn完成此操作。

但是,您可能需要考虑使用构建器模式:

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

现在poco本身可以是不可变的,但构建器可以在任意位置设置属性。

哎呀,如果你是隐式转换的粉丝,你甚至可以从构建器到poco的隐式转换:

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

虽然我所有的工具都是为了验证正确的编码而不能设计一种类型来避免它被误用,但我更喜欢开始使用以下特性的API:)

另请注意,对于C#4中的命名参数,甚至可以使构造函数版本变得清晰:

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

但当然仍然允许人在没有命名参数的情况下编写它...