我希望使用以下测试
强制执行我的代码库不可变规则[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");
}
}
传递给:
public class Entity
{
private int ID1;
public int ID
{
get { return ID1; }
}
}
但不是为了这个:
public class Entity
{
public int ID { get; private set; }
}
这里有一个问题“WTF?”
答案 0 :(得分: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();
}
使用此扩展方法,您可以轻松地测试类型
typeof(MyType).IsImmutable()
和实例
myInstance.IsImmutable()
因为他们的不变性。
备注强>
readonly
字段。FiledInfo.FieldType.IsImmutable()
。答案 1 :(得分:3)
您可能需要致电
propertyInfo.GetSetMethod().IsPublic
因为set-method和get-method没有相同的访问修饰符,所以不能依赖PropertyInfo本身。
var setterCount =
(from s in typeof (Entity).GetProperties(
BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance)
where
s.GetSetMethod() != null // public setter available
select s)
答案 2 :(得分:3)
对其他地方发布的答案进行小修改。如果至少有一个属性具有受保护或公共setter,则以下内容将返回非零值。请注意检查GetSetMethod返回null(无setter)和IsPrivate测试(即不公开或受保护)而不是IsPublic(仅限公共)。
var setterCount =
(from s in typeof(Entity).GetProperties(
BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance)
where
s.GetSetMethod(true) != null // setter available
&& (!s.GetSetMethod(true).IsPrivate)
select s).Count();
尽管如此,作为pointed out in Daniel Brückner's answer,一个类没有公开可见的属性设置器这一事实是一个必要的,但不足以使该类被认为是不可变的。
答案 3 :(得分:2)
我认为原因是CanWrite说如果有一个setter它会返回true。私人二传手也是一个二传手。
让我感到惊讶的是,第一次传递,因为它有一个公共设置器,所以除非我仍然太低于cafeine,所以settercount是1所以断言应该失败。它们都应该失败,因为CanWrite只为两者返回true。 (并且linq查询只检索公共属性,包括ID,因为它是公共的)
(编辑)我现在看到你改变了第一个类的代码,所以它不再有一个setter了。
所以你可以假设CanWrite会查看setter方法的访问器,但事实并非如此。你应该这样做:
var setterCount =
(from s in typeof (Entity).GetProperties(BindingFlags.Public | BindingFlags.Instance)
where s.GetSetMethod().IsPublic
select s)
.Count();
答案 4 :(得分:2)
我建议将物业状况改为:
s.CanWrite && s.GetSetMethod().IsPublic
答案 5 :(得分:1)
肯定是第二个private set
。
在第一个实例中,类Entity
的实例可以通过外部类写入其ID1属性。
在后者中,setter对于类本身是私有的,因此只能从Entity中调用(即它自己的构造函数/初始化器)
在第二个中将private
从你的二传手中取出,它应该通过
答案 6 :(得分:1)
如果我理解正确,你希望实体是不可变的。 如果是,则应将测试更改为
var setterCount = (from s in typeof(string).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => p.GetSetMethod())
where s != null && s.IsPublic
select s).Count();
Assert.That(setterCount == 0, Is.True, "Immutable rule is broken");
答案 7 :(得分:0)
您是否尝试对其进行调试,以查看LINQ查询返回的CanWrite属性为true的属性是什么?显然,它正在拾取你不期望的东西。有了这些知识,您可以使您的LINQ查询更具选择性,以您希望的方式工作。 另外,我同意@Daniel Bruckner的说法,除非字段也是只读的,否则你的课程不是完全可变的。调用者可以调用一个方法或使用一个getter,它可以通过getter在内部修改公开公开的私有字段,这会打破不可变的规则,让客户感到意外,并导致问题。对可变对象的操作不应该有副作用。