Skip to content

Property interception

Alexei Stryker edited this page Jun 27, 2018 · 12 revisions

There are 2 types of property interceptors; the IPropertyGetterInterceptor and the IPropertySetterInterceptor.
The setter and getter interceptors can be combined into one attribute if it is required to intercept both getter and setter. An interface that combines both interfaces also exists: IPropertyInterceptor.
It is also possible to force the weaver to initialize the property interceptors on object construction. To enable this, the interceptor has to implement the IPropertyInterceptorInitialize interface.

Example

A sample implementation of a getter and setter interceptor.

    [InterceptorOptions(AlwaysCreateNewInstance = true)]
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class InterceptorGetAttribute : Attribute, IPropertyGetterInterceptor, IPropertySetterInterceptor
    {
        public void OnException(Exception e)
        {
        }

        public void OnExit()
        {
        }

        public void OnGet(PropertyInterceptionInfo propertyInterceptionInfo, object value)
        {
            if (value == null || value.ToString() == "Hello")
                propertyInterceptionInfo.SetValue("New String");
        }

        public bool OnSet(PropertyInterceptionInfo propertyInterceptionInfo, object oldValue, object newValue)
        {
            if (ComparerUtils.Equals(oldValue, newValue))
                return true;

            return false;
        }
    }

Your code:

    public class SampleClass
    {
        [PropertyInterceptor]
        public string SampleProperty { get; private set; }
    }

What gets compiled:

    public class SampleClass
    {
        [NonSerialized]
        private PropertyInterceptionInfo propertyinterceptioninfo;

        [CompilerGenerated]
        private string sampleProperty;

        public string SampleProperty
        {
            get
            {
                var propertyInterceptorAttribute = new PropertyInterceptorAttribute();

                if(this.propertyinterceptioninfo == null)
                {
                    this.propertyinterceptioninfo = new PropertyInterceptionInfo(
                        methodof(get_SampleProperty), 
                        methodof(set_SampleProperty), 
                        "SampleProperty", 
                        typeof(string), 
                        this, 
                        null, 
                        new Action<object>(this.m_setteraction));
                }

                try
                {
                    propertyInterceptorAttribute.OnGet(this.propertyinterceptioninfo, this.sampleProperty);
                    return this.sampleProperty;
                }
                catch (Exception e)
                {
                    if(propertyInterceptorAttribute.OnException(e))
                        throw;
                }
                finally
                {
                    propertyInterceptorAttribute.OnExit();
                }
            }
            private set
            {
                PropertyInterceptorAttribute propertyInterceptorAttribute = new PropertyInterceptorAttribute();

                if(this.propertyinterceptioninfo == null)
                {
                    this.propertyinterceptioninfo = new PropertyInterceptionInfo(
                        methodof(get_SampleProperty), 
                        methodof(set_SampleProperty), 
                        "SampleProperty", 
                        typeof(string), 
                        this, 
                        null, 
                        new Action<object>(this.m_setteraction));
                }

                try
                {
                    if (!propertyInterceptorAttribute.OnSet(this.propertyinterceptioninfo, this.sampleProperty, value))
                    {
                        this.sampleProperty = value;
                    }
                }
                catch (Exception e)
                {
                    if(propertyInterceptorAttribute.OnException(e))
                         throw;
                }
                finally
                {
                    propertyInterceptorAttribute.OnExit();
                }
            }
        }

        private void m_setteraction(object value)
        {
            this.sampleProperty = Convert.ToString(value);
        }
    }

Types that implements the IEnumerable<> interface will also have a specialized implementation in the setter action method.
For example in the case of the following:

    [PropertyInterceptor]
    public List<string> Property { get; set; }

This is whats gets compiled for the setter action:

    private void <Property>m_setteraction(object value)
    {
        if (this.<Property>k__BackingField == null)
        {
            this.<Property>k__BackingField = new List<string>();
        }

        if (value == null)
        {
            this.<Property>k__BackingField.Clear();
        }
        else
        {
            var values = (value as IEnumerable).Cast<string>().ToArray();
            for(int i=0; i < values.Length; i++)
                 this.<Property>k__BackingField.Add(values[i]);
        }
    }

Abstract properties

Applying the property interceptor to an abstract property will result in all overriding properties implementing the interceptor.

    public abstract class SampleClassBase
    {
        [PropertyInterceptor]
        public abstract string SampleProperty { get; }
    }

The weaver will copy the interceptor to all property implementation.

    public class CoolClass : SampleClassBase
    {
        [PropertyInterceptor]
        public override string SampleProperty { get; }
    }