When the BindingFlags.ExactBinding binding flag is used, reflection models the accessibility rules of the common type system. For example, if the caller is in the same assembly, the caller does not need special permissions for internal members. Otherwise, the caller needs ReflectionPermission. This is consistent with the lookup of members that are protected, private, and so on.
This leads to the following reflective access incorrectly being marked as bypassing accessibility when that is not the case:
var type = object.GetType();
var methodHandle = type.GetMethod(..., BindingFlags.NonPublic | BindingFlags.ExactBinding | BindingFlags.lnstance);
methodHandle.Invoke(object, args);
Note that this specifically references the combination of using NonPublic and ExactBinding. The following very similar code is a TP:
var type = object.GetType();
var methodHandle = type.GetMethod(..., BindingFlags.NonPublic | BindingFlags.Instance);
methodHandle.Invoke(object, args);
Why do you think this invalidates S3011? To me, the rule is about not relying on internal implementations that are open to change (of the contract), and I think BindingFlags.ExactBinding does not change that.
Only the latest version of SonarQube Community Build is considered active, so you’ll need to upgrade and see if the situation is still replicable before we can help you.
Might be that my use-case is a bit rough around the edges. Effectively I am using reflection to implement an open-generic downcast, which is … not a thing that C# supports.
consider:
private static void MCVE(ISupertype? instance, object argument)
{
var type = instance?.GetType();
var basedOff = type?.BaseType;
// figure out if we have the special implementation:
while (basedOff != null && !(basedOff.IsGenericType && basedOff.GetGenericTypeDefinition().Equals(typeof(ISpecialAbstract<>))))
{
basedOff = basedOff.BaseType;
}
if (basedOff == null) { return; }
var methodHandle = type!.GetMethod(nameof(SpecialAbstract<StubEnum>.DoAThing), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding)!;
methodHandle.Invoke(instance, [argument]);
}
public interface ISuperType
{
void SharedMethod();
}
public abstract class SpecialAbstract<T> : ISuperType
where T : Enum, struct
// struct forces usage sites of this to provide an actual enum
{
abstract void SharedMethod();
// internal for library publishing reasons.
internal void DoAThing(object someArg) => [...];
}
// this is only here because nameof doesn't support open generics (yet)
internal enum StubEnum { }
The fact that the method is internal is specifically to constrain its visibility to the assembly, while acting as public within that assembly. The API should break, but we can’t downcast the instance to a reasonable type because of the generic.
I am fully aware that this is to an extent breaking the language, but such is the hand I’ve been dealt. The whole point of this chaos is to allow me to rely on the compiler in a different place, but that’s going a bit far for this thread
Let me be clear: I’ve suppressed S3011 my self on some occasions, because I needed it. Given the fact that in those cases (and I think, also in your case) you are doing dirty stuff, I think you should suppress it, end document why this done, despite the advise not to, instead of hoping that the S3011 analyzer can reason with your (and mine in those cases) tendency to do dangerous stuff.