Один из подходов - использовать интерфейсы для управления доступом. Т.е. метод A опубликован c, но не отображается в интерфейсе. B и C получают ссылку на фактический класс, а все остальные получают интерфейс. Эту защиту можно ослабить, забросив объект, но это может затруднить случайное неправильное использование.
Другой вариант - поместить A, B и C в один и тот же проект и сделать A internal .
Третий вариант - использовать имя метода, которое дает понять, что метод следует использовать только в некоторых очень особых обстоятельствах. Если разработчик увидит метод с именем DoNotCallThisMethodFromAnythingExceptBorC
с заметным комментарием, объясняющим, почему, он, вероятно, не будет использовать его, прежде чем исследовать еще несколько.
Существует также CallerMemberName , который можно использовать для автоматического добавления вызывающего метода / имени файла.
Существует также класс StackTrace , который может быть используется для проверки вызывающего метода.
Но, пожалуйста, не используйте callerMemberName или StackTrace больше, чем для ведения журнала. Это может легко привести к ошибкам во время выполнения, которые трудно найти, и, поскольку большинство людей этим не занимается, ваши коллеги (нынешние и будущие), вероятно, будут справедливо жаловаться.