我的问题是基于这篇文章.
基本上,类可以实现Freezable方法,以确保在对象进入Frozen状态后不能更改任何属性.
我有一个遵循这种设计的界面
public interface IFreezableModel
{
void Freeze();
bool IsFrozen{get;}
}
Run Code Online (Sandbox Code Playgroud)
目标是确保一旦调用Freeze方法,IsFrozen属性设置为True,并且不再更改对象的属性.
为简化起见,我将使用一个抽象基类:
public abstract class BaseFreezableModel : IFreezableModel
{
public void Freeze()
{
_isFrozen = true;
}
public bool IsFrozen
{
get {return _isFrozen;}
}
protected ThrowIfFrozen()
{
if (IsFrozen)
throw new Exception("Attempted to change a property of a frozen model");
}
}
Run Code Online (Sandbox Code Playgroud)
这样我就可以上课了
public class MyModel : BaseFreezableModel
{
private string _myProperty;
public string MyProperty
{
get{return _myProperty;}
set
{
ThrowIfFrozen();
_myProperty = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这一切都很简单,但我可以采用哪种策略来确保所有属性都遵循上述模式?(除了写限制者和吸气剂)
这些是我提出的替代方案:
找到一种机制,可以使用emit将方法注入每个属性setter.但我不知道该怎么做,我可能遇到的潜在问题(因此需要多长时间).如果有人知道这一点并且可以指出我的方向,那就太好了.
在构建过程中使用一些模板,以便在编译时之前插入对OnCheckFrozen的调用.这具有易于理解的优点,并且可以用于类似的场景.
找到一个可以为我做所有这一切的框架,但它只是一个极端的情况,因为我不允许在这个项目上使用外部框架.
你会用什么解决方案来实现这个目标?
Mat*_*vey 17
您将在此处进入面向方面编程的世界.你可以使用PostSharp在5分钟内完成这种功能- 但似乎你不允许使用外部框架.因此,您的选择归结为实现您自己的非常简单的AOP框架,或者只是咬住子弹并向每个属性设置器添加检查.
就个人而言,我只是在财产制定者中写支票.这可能不像你期望的那样痛苦.您可以编写一个可视化工作室代码片段来加速该过程.您还可以编写一个智能单元测试类,它将使用反射扫描冻结对象的所有属性并尝试设置值 - 测试失败如果没有抛出异常..
编辑响应VoodooChilds请求..这是一个单元测试类的快速示例,使用NUnit和优秀的FluentAssertions库.
[TestFixture]
public class PropertiesThrowWhenFrozenTest
{
[TestCase(typeof(Foo))]
[TestCase(typeof(Bar))]
[TestCase(typeof(Baz))]
public void AllPropertiesThrowWhenFrozen(Type type)
{
var target = Activator.CreateInstance(type) as IFreezable;
target.Freeze();
foreach(var property in type.GetProperties())
{
this.AssertPropertyThrowsWhenChanged(target, property);
}
}
private void AssertPropertyThrowsWhenChanged(object target, PropertyInfo property)
{
// In the case of reference types, setting the property to null should be sufficient
// to test the behaviour...
object value = null;
// In the case of value types, just create a default instance...
if (property.PropertyType.IsValueType)
value = Activator.CreateInstance(property.PropertyType);
Action setter = () => property.GetSetMethod().Invoke(target, new object[] { value });
// ShouldThrow is a handy extension method of the FluentAssetions library...
setter.ShouldThrow<InvalidOperationException>();
}
}
Run Code Online (Sandbox Code Playgroud)
此方法使用参数化单元测试来传递正在测试的类型,但您可以将所有这些代码同样封装到通用基类(其中T:IFreezable)中,并为每个被测试类型创建扩展类,但是一些测试运行器不喜欢在基类中进行测试..*ahem*Resharper!啊哈
编辑2,只是为了好玩,这里是一个小黄瓜脚本的例子,可以用来为这种事情创建更灵活的测试:)
Feature: AllPropertiesThrowWhenFrozen
In order to make sure I haven't made any oversights in my code
As a software developer
I want to be able to assert that all properties of a class throw an exception when the object is frozen
Scenario: Setting the Bar property on the Foo type
Given I have an instance of the class MyNamespace.MyProject.Foo
And it is frozen
When I set the property Bar with a value of 10
Then a System.InvalidOperationException should be thrown
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2298 次 |
| 最近记录: |