| Severity | Error |
| Reported by | PostSharp |
| Message | Error while evaluating the MethodPointcut "<PointcutMethod>" on "<AspectTarget>": cannot find a parameter of "<DeclaringType>.<Member>". The pointcut method must return parameters of methods of the current assembly, not parameters of indexers. |
Cause
A MethodPointcut selects the targets of an advice with a method of the aspect class. PostSharp runs this method at build time and maps each ParameterInfo that it returns to a parameter of a method of the project. PostSharp reports LA0104 on the pointcut method, and skips the parameter, when it cannot do so:
- The parameter belongs to an indexer, because the pointcut method got it from PropertyInfo.GetIndexParameters. The Member of such a parameter is a property, not a method.
- PostSharp cannot find the method that declares the parameter in the current assembly.
<DeclaringType>.<Member> is the member that declares the parameter, for instance PriceList.Item for an indexer, and not the parameter itself.
In the following example, the pointcut method of a LocationValidationAdvice also returns the parameters of the indexers of the target type.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Reflection;
using PostSharp.Serialization;
namespace Errors.LA0104_Error;
[PSerializable]
public class NotEmptyParametersAttribute : TypeLevelAspect
{
// The pointcut method also returns the parameters of the indexers, which belong to a property, not to a method.
private IEnumerable<ParameterInfo> SelectParameters( Type type )
=> type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly )
.SelectMany( method => method.GetParameters() )
.Concat( type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ).SelectMany( property => property.GetIndexParameters() ) )
.Where( parameter => parameter.ParameterType == typeof(string) );
[LocationValidationAdvice, MethodPointcut( nameof(SelectParameters) )]
public Exception? ValidateValue( string? value, string locationName, LocationKind locationKind, LocationValidationContext context )
=> string.IsNullOrEmpty( value ) ? new ArgumentException( $"The parameter {locationName} cannot be empty.", locationName ) : null;
}
[NotEmptyParameters]
public class PriceList
{
private readonly Dictionary<string, decimal> prices = new() { ["C-42"] = 9.99m };
public decimal this[string productId] => this.prices[productId];
}
public static class Program
{
public static void Main()
{
var priceList = new PriceList();
Console.WriteLine( priceList["C-42"] );
try
{
Console.WriteLine( priceList[""] );
}
catch ( ArgumentException e )
{
Console.WriteLine( e.Message );
}
}
}
error LA0104: LA0104_Error.cs(16,40): Error while evaluating the MethodPointcut "Errors.LA0104_Error.NotEmptyParametersAttribute.SelectParameters(System.Type)" on "Errors.LA0104_Error.PriceList": cannot find a parameter of "Errors.LA0104_Error.PriceList.Item". The pointcut method must return parameters of methods of the current assembly, not parameters of indexers.
Resolution
Return the parameters of methods only. To select the parameters of an indexer, return the parameters of its accessors, such as get_Item: the methods of the type returned by Type.GetMethods already include them.
In the following example, the pointcut method returns the parameters of the methods of PriceList, which include the parameter of the getter of its indexer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Reflection;
using PostSharp.Serialization;
namespace Errors.LA0104_Fixed;
[PSerializable]
public class NotEmptyParametersAttribute : TypeLevelAspect
{
// The parameters of the methods include those of the indexer accessors, such as get_Item.
private IEnumerable<ParameterInfo> SelectParameters( Type type )
=> type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly )
.SelectMany( method => method.GetParameters() )
.Where( parameter => parameter.ParameterType == typeof(string) );
[LocationValidationAdvice, MethodPointcut( nameof(SelectParameters) )]
public Exception? ValidateValue( string? value, string locationName, LocationKind locationKind, LocationValidationContext context )
=> string.IsNullOrEmpty( value ) ? new ArgumentException( $"The parameter {locationName} cannot be empty.", locationName ) : null;
}
[NotEmptyParameters]
public class PriceList
{
private readonly Dictionary<string, decimal> prices = new() { ["C-42"] = 9.99m };
public decimal this[string productId] => this.prices[productId];
}
public static class Program
{
public static void Main()
{
var priceList = new PriceList();
Console.WriteLine( priceList["C-42"] );
try
{
Console.WriteLine( priceList[""] );
}
catch ( ArgumentException e )
{
Console.WriteLine( e.Message );
}
}
}
9.99
The parameter productId cannot be empty. (Parameter 'productId')
See Also
Reference
MethodPointcut
LocationValidationAdvice
Other Resources