Open sandboxFocusImprove this doc

AR0102: Component-Internal Declaration Used Outside of Its Component

Severity Warning
Reported by PostSharp
Message <ReferencedKind> <ReferencedDeclaration> cannot be referenced from <ReferencingKind> <ReferencingDeclaration> because of the [ComponentInternal] constraint.

Cause

The ComponentInternalAttribute constraint restricts the use of a declaration to a component, a visibility between internal and private. With the default constructor, the component is the namespace of the type that declares the declaration. The other constructors take a list of friend types or namespaces instead; a friend namespace includes its child namespaces. Code in the same top-level type as the declaration always has access.

PostSharp reports AR0102 on each method or type that references the declaration from outside of the component: it calls the method, reads the field, derives from the type, or uses the type in a signature. <ReferencingDeclaration> is the method or type that holds the reference, and the message is reported at its location, not at the line of the reference.

AR0102 is a warning by default. Its severity comes from the Severity property of the constraint, which you can set to Error.

In the following example, only InvoiceServices can call InvoiceRepository.FetchAllForCustomer, but CustomerServices calls it too.

using System;
using System.Collections.Generic;
using System.Linq;
using PostSharp.Constraints;

namespace Errors.AR0102_Error;

public record Invoice( Guid CustomerId, decimal Total );

public class InvoiceRepository
{
    // Only InvoiceServices can call this method.
    [ComponentInternal( typeof(InvoiceServices) )]
    public IEnumerable<Invoice> FetchAllForCustomer( Guid customerId )
    {
        return [new Invoice( customerId, 100 ), new Invoice( customerId, 250 )];
    }
}

public class InvoiceServices
{
    public int CountInvoices( Guid customerId ) => new InvoiceRepository().FetchAllForCustomer( customerId ).Count();
}

public class CustomerServices
{
    // This method calls InvoiceRepository.FetchAllForCustomer directly.
    public int CountInvoices( Guid customerId )
    {
        return new InvoiceRepository().FetchAllForCustomer( customerId ).Count();
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( $"Invoices: {new CustomerServices().CountInvoices( Guid.NewGuid() )}" );
    }
}
warning AR0102: AR0102_Error.cs(28,16): Method Errors.AR0102_Error.InvoiceRepository.FetchAllForCustomer(System.Guid) cannot be referenced from method Errors.AR0102_Error.CustomerServices.CountInvoices(System.Guid) because of the [ComponentInternal] constraint.

Resolution

  • Go through the public API of the component, which is allowed to use the declaration, instead of using the declaration directly.
  • When the referencing type belongs to the component, add it, or its namespace, to the friend types or namespaces of the constraint.
  • When the reference is an exception that you accept, suppress the warning on the referencing declaration with SuppressWarningAttribute. This does not work when the severity of the constraint is Error.

In the following example, CustomerServices counts the invoices through InvoiceServices.

using System;
using System.Collections.Generic;
using System.Linq;
using PostSharp.Constraints;

namespace Errors.AR0102_Fixed;

public record Invoice( Guid CustomerId, decimal Total );

public class InvoiceRepository
{
    // Only InvoiceServices can call this method.
    [ComponentInternal( typeof(InvoiceServices) )]
    public IEnumerable<Invoice> FetchAllForCustomer( Guid customerId )
    {
        return [new Invoice( customerId, 100 ), new Invoice( customerId, 250 )];
    }
}

public class InvoiceServices
{
    public int CountInvoices( Guid customerId ) => new InvoiceRepository().FetchAllForCustomer( customerId ).Count();
}

public class CustomerServices
{
    // This method goes through InvoiceServices, which can call the repository.
    public int CountInvoices( Guid customerId )
    {
        return new InvoiceServices().CountInvoices( customerId );
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( $"Invoices: {new CustomerServices().CountInvoices( Guid.NewGuid() )}" );
    }
}
Invoices: 2

See Also

Reference

ComponentInternalAttribute
SuppressWarningAttribute

Other Resources

Controlling Component Visibility Beyond Private and Internal