Зачем использовать открытый метод во внутреннем классе?
в одном из наших проектов есть много кода, который выглядит так:
internal static class Extensions
{
public static string AddFoo(this string s)
{
if (!string.IsNullOrEmpty(s)) return s + "Foo";
return "Foo";
}
}
есть ли какая-либо явная причина для этого, кроме "легче сделать тип общедоступным позже?"
Я подозреваю, что это имеет значение только в очень странных краевых случаях (отражение в Silverlight) или вообще не имеет значения.
10 ответов:
обновление: этот вопрос был тема моего блога в сентябре 2014 года. Спасибо за отличный вопрос!
существует значительная дискуссия по этому вопросу даже в самой команде компилятора.
во-первых, это мудро, чтобы понять правила. Открытый член класса или структуры-это член, который доступен ко всему, что может получить доступ к содержащему типу. Таким образом, публичный член внутреннего класса эффективно внутренний.
Итак, теперь, учитывая внутренний класс, должны ли его члены, к которым вы хотите получить доступ в сборке, быть помечены как открытые или внутренние?
мое мнение таково: отмечайте таких членов как публика.
Я использую "public", чтобы означать"этот член не является деталью реализации". Защищенный член-это деталь реализации; в нем есть что-то, что понадобится для работы производного класса. Внутренний элемент-это деталь реализации; что-то еще внутренняя часть этой сборки нуждается в члене для правильной работы. Открытый член говорит: "этот член представляет ключ, документированную функциональность, предоставляемую этим объектом."
в принципе, мое отношение таково: предположим, я решил сделать этот внутренний класс публичным классом. Для того чтобы сделать это, я хочу изменить ровно одна штука: доступность класса. Если превращение внутреннего класса в открытый класс означает, что я должен также превратить внутренний член в публичный член, то этот член был частью общественная площадь класса, и он должен был быть публичным в первую очередь.
другие люди не согласны. Есть контингент, который говорит, что они хотят иметь возможность взглянуть на объявление члена и сразу узнать, будет ли он вызываться только из внутреннего кода.
к сожалению, это не всегда хорошо работает; например, внутренний класс, который реализует внутренний интерфейс по-прежнему должен иметь элементы реализации, помеченные как public, потому что они являются частью публичной поверхности класса.
если класс
internal, с точки зрения доступности не имеет значения, отмечаете ли вы методinternalилиpublic. Однако по-прежнему хорошо использовать тип, который вы бы использовали, если бы класс былpublic.хотя некоторые говорят, что это облегчает переходы от
internalдоpublic. Он также служит частью описания метода.Internalметоды обычно считаются небезопасными для беспрепятственного доступа, в то время какpublicметоды считаются (в основном) Бесплатная игра.С помощью
internalилиpublicкак и вpublicкласс, вы гарантируете, что вы сообщаете, какой стиль доступа ожидается, а также облегчаете работу, необходимую для создания классаpublicв будущем.
Я часто отмечаю свои методы во внутренних классах public вместо internal как a) это действительно не имеет значения и b) я использую internal, чтобы указать, что метод является внутренним по назначению (есть некоторая причина, по которой я не хочу раскрывать этот метод в общедоступном классе. Поэтому, если у меня есть внутренний метод, я действительно должен понять причину, почему он является внутренним, прежде чем менять его на общедоступный, тогда как если я имею дело с открытым методом во внутреннем классе, мне действительно нужно подумать о том, почему класс является внутренним, а не почему каждый метод является внутренним.
Я подозреваю, что "легче сделать вид публичных позже?" это.
правила определения области означают, что метод будет виден только как
internal- Так что это действительно не имеет значения, отмечены ли методыpublicилиinternal.одна возможность, которая приходит на ум, что класс был public и позже был изменен на
internalи разработчик не потрудился изменить все модификаторы доступности метода.
в некоторых случаях также может быть, что внутренний тип реализует открытый интерфейс, который будет означать, что любые методы, определенные на этом интерфейсе, все равно должны быть объявлены как открытые.
Это то же самое, открытый метод будет действительно помечен как внутренний, так как он находится внутри внутреннего класса, но у него есть преимущество(как вы предположили), если вы хотите пометить класс как открытый, вам нужно изменить меньше кода.
internalговорит, что элемент может быть доступен только из той же сборки. Другие классы в этой сборке могут получить доступ кinternal publicчлен, но не сможет получить доступ кprivateилиprotectedчленinternalили нет.
Я на самом деле боролся с этим сегодня. До сих пор я бы сказал, что методы должны быть помечены
internalесли классinternalи рассматривал бы что-нибудь еще просто плохое кодирование или лень, особенно в развитии предприятия; однако мне пришлось подкласс apublicкласс и переопределить один из его методов:internal class SslStreamEx : System.Net.Security.SslStream { public override void Close() { try { // Send close_notify manually } finally { base.Close(); } } }метод должен быть
publicи это заставило меня думать, что на самом деле нет логического смысла устанавливать методы какinternalесли они действительно должно быть, как сказал Эрик Липперт.до сих пор я никогда не переставал думать об этом, я просто принял это, но после прочтения сообщения Эрика это действительно заставило меня задуматься, и после долгих размышлений это имеет большой смысл.
есть разница. В нашем проекте мы сделали много классов внутренними, но мы делаем модульный тест в другой сборке, и в нашей информации о сборке мы использовали InternalsVisibleTo, чтобы позволить сборке UnitTest вызывать внутренние классы. Я заметил, что если внутренний класс имеет внутренний конструктор, мы не можем создать экземпляр с помощью активатора.CreateInstance в сборке модульного теста по какой-то причине. Но если мы изменим конструктор на public, но класс все еще является внутренним, он работает штраф. Но я думаю, что это очень редкий случай (как сказал Эрик в оригинальном посте: отражение).
Я думаю, что у меня есть еще одно мнение по этому поводу. Сначала мне было интересно, как имеет смысл объявлять что-то публично во внутреннем классе. Тогда я оказался здесь, читая, что это может быть хорошо, если вы позже решите изменить класс на публичный. Истинный. Так,pattern сформировалось у меня в голове:если это не изменяет текущее поведение, то будьте разрешительными и разрешайте вещи, которые не имеют смысла (и не вредят) в текущем состоянии кода, но позже это будет, если вы измените объявление класса.
такой:
public sealed class MyCurrentlySealedClass { protected void MyCurretlyPrivateMethod() { } }согласно "шаблону", о котором я упоминал выше, это должно быть совершенно нормально. Это следует той же самой идее. Он ведет себя как
privateметод, так как вы не можете наследовать класс. Но если вы удалитеsealedограничение, оно по-прежнему действует: унаследованные классы могут видеть этот метод, что абсолютно то, что я хотел достичь. Но вы получаете предупреждение:CS0628илиCA1047. Оба они вот-вот не объявятprotectedчленовsealedкласса. Более того, я нашел полное согласие, о чем бессмысленно: 'защищенный член в запечатанном классе' предупреждение (одноэлементный класс)Итак, после этого предупреждения и связанного обсуждения я решил сделать все внутренним или меньше, во внутреннем классе, потому что он соответствует более такому мышлению, и мы не смешиваем разные "шаблоны".