| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Cannot invoke the base implementation of '<Member>' on an instance other than 'this', because C# allows base calls only on the current instance. Use InvokerOptions.Final to invoke the member on another instance. |
Cause
In a template, you can invoke a member through an invoker and choose which implementation is called with InvokerOptions. With InvokerOptions.Base, Metalama calls the implementation that precedes the current aspect layer. When this implementation is inherited from a base class, or when the member overrides or hides a base member, Metalama must generate a base.Member(...) access.
C# allows base access only on the current instance. Metalama reports LAMA0650 when you combine InvokerOptions.Base with WithObject, so that the member would be invoked on another object, such as a parameter. The <Member> placeholder is the invoked member.
In the following example, the LogComparison aspect invokes the base implementation of Vehicle.Describe on the other parameter.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.Invokers;
4using System;
5using System.Linq;
6
7namespace Doc.LAMA0650.Error;
8
9public class LogComparisonAttribute : OverrideMethodAspect
10{
11 public override dynamic? OverrideMethod()
12 {
13 var describeMethod = meta.Target.Type.BaseType!.Methods.OfName( "Describe" ).Single();
14
15 // Error: a base call is possible only on the current instance, not on 'other'.
16 var otherDescription = describeMethod
17 .WithObject( meta.Target.Parameters[0] )
18 .WithOptions( InvokerOptions.Base )
19 .Invoke();
20
21 Console.WriteLine( $"Comparing with {otherDescription}." );
22
23 return meta.Proceed();
24 }
25}
26
1namespace Doc.LAMA0650.Error;
2
3public class Vehicle
4{
5 public string Model { get; init; } = "";
6
7 public virtual string Describe() => $"Vehicle {this.Model}";
8}
9
10public class Car : Vehicle
11{
12 [LogComparison]
13 public bool IsSameModel( Car other ) => this.Model == other.Model;
14}
15
Resolution
Do one of the following:
- Use InvokerOptions.Final, or omit
WithOptions, to call the final implementation of the member on the other instance. This generates an ordinary call, such asother.Describe(), which goes through virtual dispatch. - If you really need the base implementation, invoke the member on the current instance instead of another object.
In the following example, the aspect uses InvokerOptions.Final.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.Invokers;
4using System;
5using System.Linq;
6
7namespace Doc.LAMA0650.Fixed;
8
9public class LogComparisonAttribute : OverrideMethodAspect
10{
11 public override dynamic? OverrideMethod()
12 {
13 var describeMethod = meta.Target.Type.BaseType!.Methods.OfName( "Describe" ).Single();
14
15 // Fixed: the final implementation can be invoked on any instance.
16 var otherDescription = describeMethod
17 .WithObject( meta.Target.Parameters[0] )
18 .WithOptions( InvokerOptions.Final )
19 .Invoke();
20
21 Console.WriteLine( $"Comparing with {otherDescription}." );
22
23 return meta.Proceed();
24 }
25}
26
1namespace Doc.LAMA0650.Fixed;
2
3public class Vehicle
4{
5 public string Model { get; init; } = "";
6
7 public virtual string Describe() => $"Vehicle {this.Model}";
8}
9
10public class Car : Vehicle
11{
12 [LogComparison]
13 public bool IsSameModel( Car other ) => this.Model == other.Model;
14}
15
1using System;
2
3namespace Doc.LAMA0650.Fixed;
4
5public class Vehicle
6{
7 public string Model { get; init; } = "";
8
9 public virtual string Describe() => $"Vehicle {this.Model}";
10}
11
12public class Car : Vehicle
13{
14 [LogComparison]
15 public bool IsSameModel(Car other)
16 {
17 var otherDescription = other.Describe();
18 Console.WriteLine($"Comparing with {otherDescription}.");
19 return this.Model == other.Model;
20 }
21}
22