Programmatically insert a method call on each property of a class

后端 未结 4 1310
醉酒成梦
醉酒成梦 2021-02-01 20:30

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

相关标签:
4条回答
  • 2021-02-01 21:13

    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.

    0 讨论(0)
  • 2021-02-01 21:14

    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 讨论(0)
  • 2021-02-01 21:19

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

    0 讨论(0)
  • 2021-02-01 21:26

    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
    
    0 讨论(0)
提交回复
热议问题