This very sample aspect overrides the target field or property so that any attempt to set it to one of the forbidden value is simply ignored.
Implementation
The aspect class is derived from FieldOrPropertyAspect, which itself derives from Attribute.
The aspect constructor accepts the list of forbidden values, and stores them as a field.
8private readonly object?[] _ignoredValues;
9
10public IgnoreValuesAttribute(params object?[] values)
11{
12 this._ignoredValues = values;
13}
The OverrideProperty property is the template overriding the original property. The getter implementation, get => meta.Proceed()
, means that the getter is not modified. In the setter, we have a compile-time foreach
loop that, for each forbidden value, tests if the assigned value is equal to this forbidden value and, if it is the case, returns before calling meta.Proceed()
, i.e. before assigning the underlying field.
15
16public override dynamic? OverrideProperty
17{
18 get => meta.Proceed();
19 set
20 {
21 foreach (var ignoredValue in this._ignoredValues)
22 {
23 if (value == meta.RunTime(ignoredValue))
24 {
25 return;
26 }
27 }
28
29 meta.Proceed();
30 }
31}
32
This simple approach works well for most types you can use in an attribute constructor, but not for all of them:
- For enums (except .NET Standard 2.0 enums), the constructor will receive the underlying integer value instead of a typed value. This means that our comparison will generate invalid C# because it will compare an enum to an integer.
- For arrays, a simple
==
comparison is not sufficient.
Both cases could be handled by a more complex aspect. However, in this example, we will simply prevent the aspect from being applied to fields or properties of an unsupported type. We achieve this by implementing the BuildEligibility method.
33public override void BuildEligibility(IEligibilityBuilder<IFieldOrProperty> builder)
34{
35 var supportedTypes =
36 new[]
37 {
38 typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float),
39 typeof(double), typeof(decimal), typeof(short), typeof(sbyte), typeof(byte),
40 typeof(ushort), typeof(char), typeof(string), typeof(bool), typeof(Type)
41 };
42
43 builder.Type().MustSatisfyAny(supportedTypes.Select(supportedType =>
44 new Action<IEligibilityBuilder<IType>>(t => t.MustBe(supportedType))).ToArray());
45}
Complete source code
Here is the complete source code of the aspect.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Eligibility;
4
5internal class IgnoreValuesAttribute : OverrideFieldOrPropertyAspect
6{
7 //
8 private readonly object?[] _ignoredValues;
9
10 public IgnoreValuesAttribute(params object?[] values)
11 {
12 this._ignoredValues = values;
13 }
14 //
15
16 public override dynamic? OverrideProperty
17 {
18 get => meta.Proceed();
19 set
20 {
21 foreach (var ignoredValue in this._ignoredValues)
22 {
23 if (value == meta.RunTime(ignoredValue))
24 {
25 return;
26 }
27 }
28
29 meta.Proceed();
30 }
31 }
32
33 public override void BuildEligibility(IEligibilityBuilder<IFieldOrProperty> builder)
34 {
35 var supportedTypes =
36 new[]
37 {
38 typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float),
39 typeof(double), typeof(decimal), typeof(short), typeof(sbyte), typeof(byte),
40 typeof(ushort), typeof(char), typeof(string), typeof(bool), typeof(Type)
41 };
42
43 builder.Type().MustSatisfyAny(supportedTypes.Select(supportedType =>
44 new Action<IEligibilityBuilder<IType>>(t => t.MustBe(supportedType))).ToArray());
45 }
46}