Open sandboxFocusImprove this doc

LA0109: Method Pointcut Selects a Member of a Generic Type Instance

Severity Error
Reported by PostSharp
Message Error while evaluating the MethodPointcut "<PointcutMethod>" on "<AspectTarget>": cannot apply an advice to the generic type instance or generic method instance "<Element>". Select the members of the generic type definition or of the generic method definition instead.

Cause

An advice of a type-level aspect can select its targets with MethodPointcut, which names a method of the aspect that returns the declarations to enhance. PostSharp adds the advice to the code of each declaration, and code exists only once for a generic type or method: in its definition, such as Repository<T>, not in each instance, such as Repository<Customer>.

PostSharp reports LA0109 for each element that the pointcut method returns when the element is a generic type instance, a member of a generic type instance, or a generic method instance. The most common cause is a pointcut method that reads the members of a base type: when the target type derives from Repository<Customer>, its BaseType is the generic type instance, and so are the members it returns. The error is reported on the pointcut method, once for each such element, and the element is skipped.

In the following example, the pointcut method selects the methods of the base type of CustomerRepository.

using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0109_Error;

[PSerializable]
public class TraceAttribute : TypeLevelAspect
{
    // The methods of the base type belong to Repository<Customer>, a generic type instance.
    private IEnumerable<MethodInfo> SelectMethods( Type type )
        => type.BaseType!.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );

    [OnMethodEntryAdvice, MethodPointcut( nameof(SelectMethods) )]
    public void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering {args.Method.DeclaringType!.Name}.{args.Method.Name}." );
    }
}

public class Customer
{
    public string Name { get; set; } = "";
}

public class Repository<T>
{
    private readonly List<T> items = new();

    public void Add( T item ) => this.items.Add( item );

    public IReadOnlyList<T> GetAll() => this.items;
}

[Trace]
public class CustomerRepository : Repository<Customer>
{
    public Customer? FindByName( string name )
    {
        foreach ( var customer in this.GetAll() )
        {
            if ( customer.Name == name )
            {
                return customer;
            }
        }

        return null;
    }
}

public static class Program
{
    public static void Main()
    {
        var repository = new CustomerRepository();
        repository.Add( new Customer { Name = "Alice" } );
        Console.WriteLine( repository.FindByName( "Alice" )?.Name );
    }
}
error LA0109: LA0109_Error.cs(14,37): Error while evaluating the MethodPointcut "Errors.LA0109_Error.TraceAttribute.SelectMethods(System.Type)" on "Errors.LA0109_Error.CustomerRepository": cannot apply an advice to the generic type instance or generic method instance "Errors.LA0109_Error.Repository`1<T>.Add(`0)". Select the members of the generic type definition or of the generic method definition instead.
error LA0109: LA0109_Error.cs(14,37): Error while evaluating the MethodPointcut "Errors.LA0109_Error.TraceAttribute.SelectMethods(System.Type)" on "Errors.LA0109_Error.CustomerRepository": cannot apply an advice to the generic type instance or generic method instance "Errors.LA0109_Error.Repository`1<T>.GetAll". Select the members of the generic type definition or of the generic method definition instead.

Resolution

Make the pointcut method return members of generic definitions only. Typically, apply the aspect to the generic type definition itself, and select only the members that each target type declares, with BindingFlags.DeclaredOnly.

In the following example, the aspect is applied to Repository<T> and to CustomerRepository, and each instance of the aspect selects the methods of its own target type.

using System;
using System.Collections.Generic;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0109_Fixed;

[PSerializable]
public class TraceAttribute : TypeLevelAspect
{
    // Only the methods declared by the target type itself are selected.
    private IEnumerable<MethodInfo> SelectMethods( Type type )
        => type.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );

    [OnMethodEntryAdvice, MethodPointcut( nameof(SelectMethods) )]
    public void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering {args.Method.DeclaringType!.Name}.{args.Method.Name}." );
    }
}

public class Customer
{
    public string Name { get; set; } = "";
}

// The aspect is applied to the generic type definition to trace its methods.
[Trace]
public class Repository<T>
{
    private readonly List<T> items = new();

    public void Add( T item ) => this.items.Add( item );

    public IReadOnlyList<T> GetAll() => this.items;
}

[Trace]
public class CustomerRepository : Repository<Customer>
{
    public Customer? FindByName( string name )
    {
        foreach ( var customer in this.GetAll() )
        {
            if ( customer.Name == name )
            {
                return customer;
            }
        }

        return null;
    }
}

public static class Program
{
    public static void Main()
    {
        var repository = new CustomerRepository();
        repository.Add( new Customer { Name = "Alice" } );
        Console.WriteLine( repository.FindByName( "Alice" )?.Name );
    }
}
Entering Repository`1.Add.
Entering CustomerRepository.FindByName.
Entering Repository`1.GetAll.
Alice

See Also

Reference

MethodPointcut
TypeLevelAspect

Other Resources

Selecting Members with Pointcuts