开发者

Programmatically insert a method call on each property of a class

My question is based on this article.

Basically a class can implement a Freezable method to make sure that no properties can be changed once the object enters the Frozen state.

I have an interface that follow this design

public interface IFreezableModel
{
    void Freeze();
    bool IsFrozen{get;}
}

the objective is to make sure that once the Freeze method is called, the IsFrozen property is set to True and the properties of the object cannot be changed anymore.

To simplify, I will be using an abstract base class:

public abstract class BaseFreezableModel : IFreezableModel
{
    public void Freeze()
    {
        _isFrozen = true;
    }
    public bool IsFro开发者_如何学Gozen
    { 
       get {return _isFrozen;}            
    }
    protected ThrowIfFrozen()
    {
       if (IsFrozen)
           throw new Exception("Attempted to change a property of a frozen model");
    }
}

this way I can have a class like

public class MyModel : BaseFreezableModel
{
     private string _myProperty;
     public string MyProperty
     {
        get{return _myProperty;}
        set 
        {
           ThrowIfFrozen();
           _myProperty = value;
        }
     }
 }

This is all nice and simple, but which strategy can I adopt to make sure all properties follow the pattern above? (apart from writing setters and getters)

These are the alternatives I came up with:

  • Find a mechanism to inject a method into each property setter using emit perhaps. But I have no idea how to do it, what potential issues I may encounter (and therefore how long it will take). If someone knows this and can point me in that direction, it would be great.

  • Use some templates during the build process so that the Call to OnCheckFrozen is inserted just before compile time. This has the advantage of being really simple to understand and can work for similar scenarios.

  • Find a framework that can do all of this for me, but it just as an extreme case as I am not allowed to use external framework on this project.

What solutions would you use to accomplish this?


You're entering the world of Aspect Oriented Programming here. You could knock together this kind of functionality in 5 minutes using PostSharp - but it seems you're not allowed to use external frameworks. So then your choice comes down to implementing your own very simple AOP framework, or just biting the bullet and adding checks to every property setter.

Personally I'd just write checks in ever property setter. This may not be as painful as you expect. You could write a visual studio code snippet to speed up the process.. You could also write a smart unit test class which would, using reflection, scan through all the properties of a frozen object and attempt to set a value - with the test failing if no exception was thrown..

EDIT In response to VoodooChilds request.. Here's a quick example of a unit test class, using NUnit and the excellent FluentAssertions library.

[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>();
    }
}

This method is using a parameterized unit test to pass in the types being tested, but you could equally encapsulate all of this code into a generic base class (where T : IFreezable) and create extended classes for each type being tested, but some test runners don't like having tests in base classes.. *ahem*Resharper!ahem

EDIT 2 and, just for fun, here's an example of a Gherkin script which could be used to create much more flexible tests for this kind of thing :)

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


As Matt already mentioned, you can use aspect oriented programming. Another possibility is to use a technique called interception, as it is provided by the Unity application block.


as Matt said with the addition of writing an FxCop rule to check for the method call


how about an extra bit of indirection using the proxy pattern so you can inject the frozen check there? if the object the proxy refers to is frozen throw, if not proceed. however, this means you need a proxy for every IFreezableModel (though maybe generics could overcome this) and it will apply for every class member you're accessing (or the proxy needs more complexity).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜