.net不可变对象

rus*_*der 5 c# reflection properties

我希望通过以下测试强制执行我的代码库不可变规则

[TestFixture]
public class TestEntityIf
{
    [Test]
    public void IsImmutable()
    {
        var setterCount =
            (from s in typeof (Entity).GetProperties(BindingFlags.Public | BindingFlags.Instance)
             where s.CanWrite
             select s)
                .Count();

        Assert.That(setterCount == 0, Is.True, "Immutable rule is broken");
    }
}
Run Code Online (Sandbox Code Playgroud)

它通过:

public class Entity
{
    private int ID1;
    public int ID
    {
        get { return ID1; }
    }
}
Run Code Online (Sandbox Code Playgroud)

但不是为了这个:

public class Entity
{
    public int ID { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

这里有一个问题"WTF?"

Dan*_*ner 7

问题是,由于私人制定者,该属性是公共的 - 因为公共吸气剂 - 并且它是可写的.您将不得不优化您的测试.

此外,我想补充一点,你不能保证这种方式的不变性,因为你可以修改方法中的私有数据.为了保证不变性,您必须检查是否所有字段都是只读的,并且没有自动实现的属性.

public static Boolean IsImmutable(this Type type)
{
    const BindingFlags flags = BindingFlags.Instance |
                               BindingFlags.NonPublic |
                               BindingFlags.Public;

    return type.GetFields(flags).All(f => f.IsInitOnly);
}

public static Boolean IsImmutable(this Object @object)
{
    return (@object == null) || @object.GetType().IsImmutable();
}
Run Code Online (Sandbox Code Playgroud)

使用此扩展方法,您可以轻松地测试类型

typeof(MyType).IsImmutable()
Run Code Online (Sandbox Code Playgroud)

和实例

myInstance.IsImmutable()
Run Code Online (Sandbox Code Playgroud)

因为他们的不变性.

笔记

  • 查看实例字段允许您具有可写属性,但确保现在可以修改字段.
  • 由于私有的匿名支持字段,自动实现的属性将无法通过不可变性测试而失败.
  • 您仍然可以readonly使用反射修改字段.
  • 也许应该检查所有字段的类型是否也是不可变的,因为这些对象属于状态.
  • FiledInfo.FieldType.IsImmutable()由于可能的周期以及基类型是可变的,所以对于所有字段都不能这样做.


Joe*_*Joe 3

对其他地方发布的答案进行小修改。如果至少有一个属性具有受保护或公共 setter,则以下内容将返回非零。请注意对 GetSetMethod 的检查是否返回 null(无 setter)以及对 IsPrivate(即非公共或受保护)而不是 IsPublic(仅限公共)的测试。

\n\n
    var setterCount =\n           (from s in typeof(Entity).GetProperties(\n              BindingFlags.Public\n              | BindingFlags.NonPublic\n              | BindingFlags.Instance)\n            where\n              s.GetSetMethod(true) != null // setter available\n              && (!s.GetSetMethod(true).IsPrivate)\n            select s).Count();\n
Run Code Online (Sandbox Code Playgroud)\n\n

尽管如此,正如Daniel Br\xc3\xbcckner\ 的回答所指出的,类没有公开可见的属性设置器这一事实是该类被视为不可变的必要条件,但不是充分条件。

\n