Зачем использовать публичный метод во внутреннем классе?
В одном из наших проектов много кода, который выглядит следующим образом:
internal static class Extensions
{
public static string AddFoo(this string s)
{
if (!string.IsNullOrEmpty(s)) return s + "Foo";
return "Foo";
}
}
Есть ли какая-либо явная причина, чтобы сделать это, кроме того, что "легче сделать тип общедоступным позже?"
Я подозреваю, что это имеет значение только в очень странных крайних случаях (отражение в Silverlight) или вообще не имеет значения.
12 ответов
ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога в сентябре 2014 года. Спасибо за отличный вопрос!
По этому вопросу ведутся серьезные споры даже внутри самой команды разработчиков.
Во-первых, разумно понимать правила. Открытый член класса или структуры - это член, который доступен любому, кто может получить доступ к содержащему типу. Таким образом, открытый член внутреннего класса фактически является внутренним.
Итак, теперь, учитывая внутренний класс, должны ли его члены, к которым вы хотите получить доступ в сборке, быть помечены как открытые или внутренние?
Мое мнение таково: отметьте таких участников как общедоступных.
Я использую "public" для обозначения "этот элемент не является деталью реализации". Защищенный элемент - это деталь реализации; в этом есть что-то, что понадобится, чтобы заставить производный класс работать. Внутренний член - это деталь реализации; что-то еще внутри этой сборки нуждается в члене для правильной работы. Публичный член говорит: "Этот член представляет ключевую, документированную функциональность, предоставляемую этим объектом"
По сути, мое отношение таково: предположим, я решил превратить этот внутренний класс в публичный класс. Чтобы сделать это, я хочу изменить только одно: доступность класса. Если превращение внутреннего класса в публичный класс означает, что я должен также превратить внутренний член в публичный, то этот член был частью публичной области класса, и он должен был быть в первую очередь публичным.
Другие люди не согласны. Существует контингент, который говорит, что они хотят иметь возможность взглянуть на объявление члена и сразу узнать, будет ли он вызываться только из внутреннего кода.
К сожалению, это не всегда хорошо получается; например, внутренний класс, который реализует внутренний интерфейс, все еще должен иметь члены реализации, помеченные как публичные, потому что они являются частью публичной поверхности класса.
Если класс internal
, с точки зрения доступности не имеет значения, помечаете ли вы метод internal
или же public
, Однако все еще хорошо использовать тип, который вы бы использовали, если бы класс public
,
Хотя некоторые говорят, что это облегчает переходы от internal
в public
, Он также служит частью описания метода. Internal
методы обычно считаются небезопасными для беспрепятственного доступа, в то время как public
методы считаются (в основном) бесплатной игрой.
Используя internal
или же public
как вы бы в public
класс, вы убедитесь, что вы сообщаете, какой стиль доступа ожидается, а также облегчает работу, необходимую для создания класса public
в будущем.
Я часто отмечаю свои методы во внутренних классах как публичные, а не внутренние как: а) это на самом деле не имеет значения, и б) я использую внутренний, чтобы указать, что метод является внутренним по назначению (есть некоторая причина, почему я не хочу раскрывать это Метод в публичном классе. Поэтому, если у меня есть внутренний метод, я действительно должен понять причину, по которой он является внутренним, прежде чем менять его на публичный, тогда как, если я имею дело с публичным методом во внутреннем классе, мне действительно нужно подумать, почему класс является внутренним, в отличие от того, почему каждый метод является внутренним.
Я подозреваю, что "легче сделать тип позже общедоступным?" это.
Правила области видимости означают, что метод будет виден только как internal
- так что действительно не имеет значения, помечены ли методы public
или же internal
,
Одна возможность, которая приходит в голову, состоит в том, что класс был общедоступным и был позже изменен internal
и разработчик не удосужился изменить все модификаторы доступности метода.
В некоторых случаях может также оказаться, что внутренний тип реализует открытый интерфейс, что будет означать, что любые методы, определенные в этом интерфейсе, все равно должны быть объявлены как публичные.
То же самое, публичный метод будет действительно помечен как внутренний, так как он находится внутри внутреннего класса, но у него есть преимущество (как вы уже догадались): если вы хотите пометить класс как открытый, вы должны изменить меньше кода.
По той же причине, что и использование общедоступных методов в любом другом классе - чтобы они были общедоступными вне содержащего его типа.
Модификатор доступа Type не имеет ничего общего с модификаторами доступа его членов. Оба решения принимаются совершенно независимо.
Тот факт, что определенные комбинации модификаторов типа и членов производят на первый взгляд (или, как другие называют это «эффективно») один и тот же результат, не означает, что они семантически одинаковы.
Модификатор локального доступа объекта (как объявлено в коде) и его глобальный эффективный уровень доступа (как оценивается через цепочку сдерживания) - это тоже совершенно разные вещи. Открытый офис внутри запертого здания по-прежнему открыт, даже если войти в него с улицы невозможно.
Не думайте о конечном эффекте. Сначала подумайте о том, что вам нужно на местном уровне.
-
: классическая ситуация. -
: type является общедоступным, но вам нужен полулегальный доступ к сборке, чтобы делать какие-то нелепые вещи. - : вы скрываете весь тип, но в сборке он имеет классическую публичную поверхность
- : Я не могу вспомнить ни одного примера из реального мира. Может быть, что-то скоро станет внутренним достоянием общественности?
Для вашего примера это не актуально, но для классов, в которых интерфейсы реализованы на C# до 7.3 включительно, это:
internal interface IAmOnlyInternalViewable
{
void CallMeOnlyInsideAssembly();
//internal void CallMeOnlyInsideAssembly(); // <-- is not possible up to C# 7.3 inclusive
}
internal class OnlyInternalVisible : IAmOnlyInternalViewable
{
public void CallMeOnlyInsideAssembly() { } //Must be public to implement the interface
}
Итак, если вы хотите иметьinternal class
, который реализуетinterface
, реализованные методы должны бытьpublic
.
Я думаю, что у меня есть дополнительное мнение по этому вопросу. Сначала мне было интересно, как имеет смысл объявить что-то публично во внутреннем классе. Затем я оказался здесь, читая, что было бы хорошо, если бы вы позже решили сменить класс на общедоступный. Правда. Итак, шаблон сформировался у меня в голове: если он не меняет текущее поведение, то будьте терпеливы и допускайте вещи, которые не имеют смысла (и не причиняют вреда) в текущем состоянии кода, но позже это будет, если вы изменить объявление класса.
Как это:
public sealed class MyCurrentlySealedClass
{
protected void MyCurretlyPrivateMethod()
{
}
}
В соответствии с "шаблоном", который я упомянул выше, это должно быть совершенно нормально. Это следует той же идее. Ведет себя как private
метод, так как вы не можете наследовать класс. Но если вы удалите sealed
ограничение, оно все еще действует: унаследованные классы могут видеть этот метод, и это абсолютно то, чего я хотел достичь. Но вы получаете предупреждение: CS0628
, или же CA1047
, Оба они вот-вот не объявят protected
члены в sealed
учебный класс. Более того, я нашел полное согласие, о том, что это бессмысленно: предупреждение "Защищенный член в запечатанном классе" (одиночный класс)
Поэтому после того, как это предупреждение и дискуссия были связаны, я решил сделать все внутренним или менее внутренним, потому что оно больше соответствует такому мышлению, и мы не смешиваем разные "шаблоны".
Там есть разница. В нашем проекте мы сделали много внутренних классов, но мы проводим модульное тестирование в другой сборке, и в нашей информации о сборке мы использовали InternalsVisibleTo, чтобы позволить сборке UnitTest вызывать внутренние классы. Я заметил, что если во внутреннем классе есть внутренний конструктор, мы не можем по какой-то причине создать экземпляр, используя Activator.CreateInstance в сборке модульного теста. Но если мы изменим конструктор на public, но класс все еще будет внутренним, он будет работать нормально. Но я думаю, что это очень редкий случай (как сказал Эрик в оригинальном сообщении: "Отражение").
Я на самом деле боролся с этим сегодня. До сих пор я бы сказал, что все методы должны быть помечены internal
если класс был internal
и считал бы все остальное просто плохим кодированием или ленью, особенно в развитии предприятий; Тем не менее, я должен был подкласс public
Класс и переопределить один из его методов:
internal class SslStreamEx : System.Net.Security.SslStream
{
public override void Close()
{
try
{
// Send close_notify manually
}
finally
{
base.Close();
}
}
}
Метод ДОЛЖЕН быть public
и это заставило меня задуматься о том, что на самом деле нет логического смысла устанавливать методы как internal
если они действительно должны быть, как сказал Эрик Липперт.
До сих пор я никогда не задумывался об этом, я просто принял это, но после прочтения поста Эрика это действительно заставило меня задуматься, и после долгих размышлений это имеет большой смысл.
internal
говорит, что к члену можно получить доступ только из одной и той же сборки. Другие классы в этой сборке могут получить доступ к internal public
член, но не сможет получить доступ к private
или же protected
член, internal
или нет.