目的
如何通过任何方法,为了单元测试的目的,确保派生类不引用基类中的任何属性?我理解Reflection won't cut it, here。我可以以某种方式创建基类的模拟,并观察是否在错误的时间调用属性?或者其他任何方式?
背景
我有一系列参与序列化的类。部件和碎片有一个自然的层次结构,例如,Chunk1
知道如何序列化自身(起始,结束,分隔符),但会将其内部部分的序列化委托给Blob
本身序列化了几行。
以下是所有部分实现的界面:
public interface ICoolSerializable {
void Serialize(Writer w);
}
并给出了所需的序列化结果:
Chunk1:/Line1
/Line2
有一个Chunk1
类负责" Chunk1:"并继承自Blob
类,后者负责" / Line1",换行符和" / Line2"。 (两者都实现ISerializable
。)
注意:为了问题,请假设我确实需要 is-a 关系,并且Chunk1
从{{1}继承是正确的(Blob
可以在许多不同的块中使用,Blob
只是确定Chunk1
的解释方式,而不是如何在初始标签之外进行序列化。
问题
我认为将来我或其他开发人员可能会写更多这样的类并尝试复制模式。由于Blob
的构造函数接受Chunk1
个IEnumerable
个项目传递给其基础Line
,因此开发人员会考虑如何构建基础,并且可能很容易Blob
序列化方法中的这个错误:
Chunk1
这会产生错误的序列化结果(缺少斜杠):
public override void Serialize(Writer w) {
w.Write("Chunk1:");
w.WriteEnumerable(Lines); // wrong, this is a forbidden base.Lines!
}
完全披露:我确实犯了这个错误,然后最初"修复"通过写作" /"在派生类的每一行之前。当然,在从基地继承的另一个类的那一刻,它也错过了斜线 - 我以错误的方式修复它。
问题
那么我如何检查Chunk1:Line1
Line2
方法或采取任何其他措施以确保永远不会从其中访问Serialize
?而不是上面的错误方式,它需要像这样工作:
base.Lines
这种模式并非全局。并非所有实现我的public override void Serialize(Writer w) {
w.Write("Chunk1:");
base.Serialize(w); // Remember to let the superclass decide how to serialize itself
}
接口的类都有子部分,也不是所有类都继承其他部分。在某些情况下,从它包装另一个类而不是子类是有意义的。
一些想法
对于那些感兴趣的人,由于字符串可以隐式转换为ICoolSerializable
,我希望我能做到这一点:
ICoolSerializable
但是,public override void Serialize(Writer w) {
w.WriteCoolSerializables,
"Chunk1:",
base
}
}
这里不能引用基本实例,如果我将当前类转换为其父类,它仍然无法工作,因为派生的base
方法(&#39} ; s Serialize
!)将被调用,从而导致循环,最终导致堆栈溢出。
更新
我怀疑正确的答案是重构,但我不确定该重构现在是如何工作的。我怀疑我可能更倾向于使用Reflection,以及序列化过程通过属性或返回的一系列属性或值访问对象,而不是程序语句。这将使得能够检查属性访问对象以查看它们所指的内容。这也可以使父类(通过属性或类似属性的方法返回信息)指示它与任何子类的关系,这种模板表示"子类可能只挂钩到我的序列化组件在头部",然后可以强制执行。
答案 0 :(得分:1)
在这种情况下,我不会让您的Serialize
方法继承。
protected void SerializeCore(Writer w) { }
public void Serialize(Writer w) {
SerializeCore(w);
...
}
这样可以控制基类的序列化方式。如果你想要更严格,你可以使用带属性的反射来执行序列化。
属性的示例基类:
public abstract class CustomSerializeAttribute : Attribute
{
public abstract void SerializeProperty(Writer w, object value);
}
答案 1 :(得分:0)
将基类中的属性设为私有。
答案 2 :(得分:0)
如果您愿意使用功能包装属性提供的功能,您可以根据黑名单或白名单检查来电者的源文件,其中哪些文件可以包含访问这些属性的代码
在Blob
实现中,对于要监视的每个属性(包装器),您可以执行以下操作:
public int GetExampleProp([System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "")
{
CheckCaller(sourceFilePath);
return ExampleProp;
}
public void SetExampleProp(int value, [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "")
{
CheckCaller(sourceFilePath);
ExampleProp = value;
}
然后在CheckCaller
private void CheckCaller(string path)
{
if (!_whitelist.Contains(path)) {
// report error
}
}