Project "Caravela" 0.3 / / Caravela Documentation / Conceptual Documentation / Creating Aspects / Creating Simple Aspects / Overriding Fields or Properties

Overriding Fields or Properties

In Overriding Methods, you have learned how to wrap an existing method with additional, automatically-generated model. You can do the same with fields and properties thanks to the OverrideFieldOrPropertyAspect abstract class.

Creating an OverrideFieldOrPropertyAspect aspect

  1. Create a new class derived from the OverrideFieldOrPropertyAspect abstract class. This class will be a custom attribute, so it is a good idea to name it with the Attribute suffix.

  2. Implement the OverrideProperty property in plain C#. The accessors of this property will serve as templates defining the way the aspect overrides the accessors of the hand-written field or property.

    • To insert code or expressions that depend on the target accessors of the aspect (such as the field or property name or type), use the meta API.
    • Where the original implementation must be invoked, call the meta.Proceed method.
  3. The aspect is a custom attribute. To transform a field or property using the aspect, just add the aspect custom attribute to the field or property.

Warning

When you apply an aspect to a field, Caravela will automatically transform the field into a property. If the field is used by reference using ref, out and in keywords, it will result in a compile-time error. (TODO #28909)

Example: An empty OverrideFieldOrPropertyAspect aspect

The next example shows an empty implementation of OverrideFieldOrPropertyAspect applied to a property and to a field.

                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework
{
    public class EmptyOverrideFieldOrPropertyAttribute : OverrideFieldOrPropertyAspect
    {
        public override dynamic OverrideProperty
        {
            get => meta.Proceed();
            set => meta.Proceed();
        }
    }
}

              
                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Caravela.Documentation.SampleCode.AspectFramework
{
    class EmptyOverrideFieldOrPropertyExample
    {
        [EmptyOverrideFieldOrProperty]
        public int Field;

        [EmptyOverrideFieldOrProperty]
        public string Property { get; set; }
    }
}

              
                using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Caravela.Documentation.SampleCode.AspectFramework
{
    class EmptyOverrideFieldOrPropertyExample
    {


        private int _field;


        public int Field
        {
            get
            {
                return this._field;
            }

            set
            {
                this._field = value;
            }
        }

        private string _property;

        [EmptyOverrideFieldOrProperty]
        public string Property
        {
            get
            {
                return this._property;
            }

            set
            {
                this._property = value;
            }
        }
    }
}
              

Accessing the metadata of the overridden field or property

The metadata of the field or property being overridden are available from the template accessors on the meta.Target.FieldOrProperty property . This property gives you all information about the name, type, parameters and custom attributes of the field or property. For instance, the member name is available on meta.Target.FieldOrProperty.Name and its type on meta.Target.FieldOrProperty.Type.

The value of the field or property is available on meta.Target.FieldOrProperty.Value. Your aspect can read and write this property, as long as the field or the property is writable. To determine if the field is readonly or if the property has a set accessor, you can use Writeability.

Example: Resolving dependencies on the fly

The following example is a simplified implementation of the service locator pattern.

The Import aspect overrides the getter of a property to make a call to a global service locator. The type of the service is determined from the type of the field or property, using meta.Target.FieldOrProperty.Type. The dependency is not stored, so the service locator must be called every time the property is evaluated.

                using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework.GlobalImport
{
    internal class ImportAttribute : OverrideFieldOrPropertyAspect
    {
        public override dynamic OverrideProperty
        {
            get => ServiceLocator.ServiceProvider.GetService(meta.Target.FieldOrProperty.Type.ToType());

            set => meta.Proceed();
        }
    }
}

              
                using System;
using System.Collections.Generic;

namespace Caravela.Documentation.SampleCode.AspectFramework.GlobalImport
{
    class TargetCode
    {
        [Import]
        IFormatProvider FormatProvider { get; }
    }

    class ServiceLocator : IServiceProvider
    {

        private static readonly ServiceLocator _instance = new();
        private readonly Dictionary<Type, object> _services = new();

        public static IServiceProvider ServiceProvider => _instance;


        object IServiceProvider.GetService(Type serviceType)
        {
            this._services.TryGetValue(serviceType, out var value);
            return value;
        }

        public static void AddService<T>(T service) => _instance._services[typeof(T)] = service;
    }

}

              
                using System;
using System.Collections.Generic;

namespace Caravela.Documentation.SampleCode.AspectFramework.GlobalImport
{
    class TargetCode
    {
        [Import]
        IFormatProvider FormatProvider
        {
            get
            {
                return (IFormatProvider)ServiceLocator.ServiceProvider.GetService(typeof(IFormatProvider));
            }
        }
    }

    class ServiceLocator : IServiceProvider
    {

        private static readonly ServiceLocator _instance = new();
        private readonly Dictionary<Type, object> _services = new();

        public static IServiceProvider ServiceProvider => _instance;


        object IServiceProvider.GetService(Type serviceType)
        {
            this._services.TryGetValue(serviceType, out var value);
            return value;
        }

        public static void AddService<T>(T service) => _instance._services[typeof(T)] = service;
    }

}
              

Example: Resolving dependencies on the fly and storing the result

This example builds over the previous one, but the dependency is stored in the field or property after it has been retrieved from the service provider for the first time.

                using System;
using Caravela.Framework.Aspects;

namespace Caravela.Documentation.SampleCode.AspectFramework.GlobalImportWithSetter
{
    internal class ImportAttribute : OverrideFieldOrPropertyAspect
    {
        public override dynamic OverrideProperty
        {
            get
            {
                // Gets the current value of the field or property.
                var service = meta.Proceed();

                if (service == null)
                {
                    // Call the service provider.
                    service =
                         meta.Cast(meta.Target.FieldOrProperty.Type,
                            ServiceLocator.ServiceProvider.GetService(meta.Target.Property.Type.ToType()));

                    // Set the field or property to the new value.
                    meta.Target.FieldOrProperty.Value = service;
                }

                return service;
            }

            set => throw new NotSupportedException();
        }
    }
}

              
                using System;
using System.Collections.Generic;

namespace Caravela.Documentation.SampleCode.AspectFramework.GlobalImportWithSetter
{
    class TargetCode
    {
        [Import]
        IFormatProvider _formatProvider { get; set; }
    }

    class ServiceLocator : IServiceProvider
    {

        private static readonly ServiceLocator _instance = new();
        private readonly Dictionary<Type, object> _services = new();

        public static IServiceProvider ServiceProvider => _instance;


        object IServiceProvider.GetService(Type serviceType)
        {
            this._services.TryGetValue(serviceType, out var value);
            return value;
        }

        public static void AddService<T>(T service) => _instance._services[typeof(T)] = service;
    }

}

              
                using System;
using System.Collections.Generic;

namespace Caravela.Documentation.SampleCode.AspectFramework.GlobalImportWithSetter
{
    class TargetCode
    {


        private IFormatProvider _formatProvider1;
        [Import]
        IFormatProvider _formatProvider
        {
            get
            {
                var service = _formatProvider_Source;
                if (service == null)
                {
                    service = (IFormatProvider)ServiceLocator.ServiceProvider.GetService(typeof(IFormatProvider));
                    this._formatProvider_Source = service;
                }

                return service;
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        private IFormatProvider _formatProvider_Source
        {
            get
            {
                return this._formatProvider1;
            }

            set
            {
                this._formatProvider1 = value;
            }
        }
    }

    class ServiceLocator : IServiceProvider
    {

        private static readonly ServiceLocator _instance = new();
        private readonly Dictionary<Type, object> _services = new();

        public static IServiceProvider ServiceProvider => _instance;


        object IServiceProvider.GetService(Type serviceType)
        {
            this._services.TryGetValue(serviceType, out var value);
            return value;
        }

        public static void AddService<T>(T service) => _instance._services[typeof(T)] = service;
    }

}