Зачем использовать открытый метод во внутреннем классе?


в одном из наших проектов есть много кода, который выглядит так:

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (!string.IsNullOrEmpty(s)) return s + "Foo";
        return "Foo";
    }
}

есть ли какая-либо явная причина для этого, кроме "легче сделать тип общедоступным позже?"

Я подозреваю, что это имеет значение только в очень странных краевых случаях (отражение в Silverlight) или вообще не имеет значения.

10 198

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 и рассматривал бы что-нибудь еще просто плохое кодирование или лень, особенно в развитии предприятия; однако мне пришлось подкласс a public класс и переопределить один из его методов:

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 класса. Более того, я нашел полное согласие, о чем бессмысленно: 'защищенный член в запечатанном классе' предупреждение (одноэлементный класс)

Итак, после этого предупреждения и связанного обсуждения я решил сделать все внутренним или меньше, во внутреннем классе, потому что он соответствует более такому мышлению, и мы не смешиваем разные "шаблоны".