【问题标题】:Can I enforce that fields in a POCO are only ever set in a type initialiser?我可以强制 POCO 中的字段只在类型初始化程序中设置吗?
【发布时间】:2014-08-10 22:57:58
【问题描述】:

如果我有这样的 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 上设置属性。我知道我可以设置设置器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 属性的方法,但我需要再往下一步检查代码块,看看它是否在对象初始化程序块中。 p>

这可能吗?

【问题讨论】:

  • 在 C# 中,接下来这将是一个“事物”(主构造函数)。在当前的 C# 中,您需要编写显式属性来执行此操作。
  • 命名参数怎么样。您可以将设置器设置为私有并使用 var myPoco = new MyPoco(firstName: "", lastName: "", address: "");
  • @MarcGravell:主要构造函数在调用代码方面无济于事,不是吗?据我所知,您将无法使用对象初始化器语法来创建实例。
  • @MarcGravell:同意,我对此感到非常高兴——但我认为这与本案中的问题并不特别相关。 (OP 声明反对将东西放入构造函数中并不是必须编写手动属性和单独的只读变量的丑陋。)
  • 使用 NDepend,您可以编写规则以确保如果方法使用 POCO 类的 setter,则它使用 POCO 类的所有 setter。但是你不能强制在对象初始化器中调用所有这些设置器。

标签: c# poco code-analysis cql ndepend


【解决方案1】:

我不知道你是否可以在 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.");

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 2011-02-15
    • 2012-03-14
    相关资源
    最近更新 更多