Open sandboxFocusImprove this doc

Getting started with overriding fields and properties

In Getting started with overriding methods, you learned how to change the implementation of a method with an aspect. You can apply the same technique to fields and properties by extending OverrideFieldOrPropertyAspect.

Overriding a field or property

Follow these steps to create an aspect capable of overriding a field or property:

  1. Add the Metalama.Framework package to your project.

  2. Create a new class derived from the OverrideFieldOrPropertyAspect abstract class. The class serves as a custom attribute, so name it with the Attribute suffix.

  3. Implement the OverrideProperty property in plain C#. To call the original implementation, use meta.Proceed.

  4. Since the aspect is a custom attribute, you can transform a field or property by simply adding the aspect custom attribute to the field or property.

Warning

When applying an aspect to a field, Metalama 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.

Example: empty OverrideFieldOrPropertyAspect aspect

The following example demonstrates an empty implementation of OverrideFieldOrPropertyAspect applied to a property and a field.

1using Metalama.Framework.Aspects;
2
3namespace Doc;
4
5public class EmptyOverrideFieldOrPropertyAttribute : OverrideFieldOrPropertyAspect
6{
7    public override dynamic? OverrideProperty
8    {
9        get => meta.Proceed();
10        set => meta.Proceed();
11    }
12}
Source Code
1namespace Doc;
2

3internal class EmptyOverrideFieldOrPropertyExample
4{
5    [EmptyOverrideFieldOrProperty]
6    public int Field;


7
8    [EmptyOverrideFieldOrProperty]













9    public string? Property { get; set; }
10}
Transformed Code
1namespace Doc;
2
3internal class EmptyOverrideFieldOrPropertyExample
4{
5    private int _field;
6
7    [EmptyOverrideFieldOrProperty]
8    public int Field
9    {
10        get
11        {
12            return _field;
13        }
14
15        set
16        {
17            _field = value;
18        }
19    }
20
21    private string? _property;
22
23    [EmptyOverrideFieldOrProperty]
24    public string? Property
25    {
26        get
27        {
28            return _property;
29        }
30
31        set
32        {
33            _property = value;
34        }
35    }
36}

This aspect performs no specific function, but it transforms the field into a property.

Getting or setting the underlying property

If you've only worked with methods so far, you're likely familiar with using the meta.Proceed() method in your template. This method also works in a property template: when called from the getter, it returns the field or property value; when called from the setter, it sets the field or property to the value of the value parameter.

To get the property value from the setter, or to set the property value to something other than the value parameter, get or set the meta.Target.FieldOrProperty.Value property.

Example: trimming strings

This aspect trims whitespace before and after string values before assigning them to the field or property.

1using Metalama.Framework.Aspects;
2
3namespace Doc.Trimmed;
4
5public class TrimAttribute : OverrideFieldOrPropertyAspect
6{
7    public override dynamic? OverrideProperty
8    {
9        get => meta.Proceed();
10        set => meta.Target.FieldOrProperty.Value = value?.Trim();
11    }
12}
Source Code
1using System;
2
3namespace Doc.Trimmed;
4
5public class Details
6{
7    [Trim]
8    public string? Code { get; set; }


9}
10




11public class Program
12{







13    public static void Main()
14    {
15        Details detail1 = new() { Code = "   GW12345  " };
16
17        Console.WriteLine( $"Code='{detail1.Code}'" );
18    }
19}
Transformed Code
1using System;
2
3namespace Doc.Trimmed;
4
5public class Details
6{
7    private string? _code;
8
9    [Trim]
10    public string? Code
11    {
12        get
13        {
14            return _code;
15        }
16
17        set
18        {
19            _code = value?.Trim();
20        }
21    }
22}
23
24public class Program
25{
26    public static void Main()
27    {
28        Details detail1 = new() { Code = "   GW12345  " };
29
30        Console.WriteLine($"Code='{detail1.Code}'");
31    }
32}
Code='GW12345'

The aspect doesn't need to modify the getter, so it only calls meta.Proceed(), and Metalama replaces this call with the original implementation of the property. You could write get => meta.Target.FieldOrProperty.Value instead, achieving the same effect.

The setter is modified to call the Trim method on the input value. The most concise and simple code is set => meta.Target.FieldOrProperty.Value = value?.Trim(). Alternatively, you could write the following code:

set
{
    value = value?.Trim();
    meta.Proceed();
}

Example: turning the value to uppercase

The following example is similar to the previous one, but instead of trimming a string, it normalizes the value to uppercase.

We apply the aspect to a class representing a shipment between two airports.

1using Metalama.Framework.Aspects;
2
3namespace Doc.UpperCase;
4
5/// <summary>
6/// Changes the value of a string property to Upper Case
7/// </summary>
8public class UpperCaseAttribute : OverrideFieldOrPropertyAspect
9{
10    public override dynamic? OverrideProperty
11    {
12        get => meta.Proceed();
13        set => meta.Target.FieldOrProperty.Value = value?.ToUpper();
14    }
15}
Source Code
1using System;
2
3namespace Doc.UpperCase;
4
5public class Shipment
6{
7    [UpperCase]
8    public string? From;


9
10    [UpperCase]













11    public string? To { get; set; }
12}
13











14public class UpperCase
15{
16    public static void Main()
17    {
18        var package = new Shipment();
19        package.From = "lhr";
20        package.To = "jfk";
21
22        Console.WriteLine( $"Package is booked from {package.From} to {package.To}" );
23    }
24}
Transformed Code
1using System;
2
3namespace Doc.UpperCase;
4
5public class Shipment
6{
7    private string? _from;
8
9    [UpperCase]
10    public string? From
11    {
12        get
13        {
14            return _from;
15        }
16
17        set
18        {
19            _from = value?.ToUpper();
20        }
21    }
22
23    private string? _to;
24
25    [UpperCase]
26    public string? To
27    {
28        get
29        {
30            return _to;
31        }
32
33        set
34        {
35            _to = value?.ToUpper();
36        }
37    }
38}
39
40public class UpperCase
41{
42    public static void Main()
43    {
44        var package = new Shipment();
45        package.From = "lhr";
46        package.To = "jfk";
47
48        Console.WriteLine($"Package is booked from {package.From} to {package.To}");
49    }
50}
Package is booked from LHR to JFK

In this example, From is a public field and To is a public property. This demonstrates that the aspect works on both because IFieldOrProperty is used in the aspect. If you want the aspect to apply only to properties and not to fields, use IProperty.

Going deeper

To go deeper into field/property overrides, explore the following articles:

  • This article shows how to use meta.Proceed and meta.Target.Method.Name in your templates. You can write much more complex and powerful templates, even performing compile-time if and foreach blocks. To learn how, see Writing T# templates.
  • To learn how to override several fields and properties from a single type-level aspect, see Overriding methods.