Подтвердить что ты не робот

Могут ли быть частные методы расширения?

Скажем, мне нужен простой частный помощник, и интуитивно в коде он будет иметь смысл в качестве метода расширения. Есть ли способ инкапсулировать этого помощника в единственный класс, который действительно должен его использовать?

Например, я пробую это:

class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}

Компилятор не видит метод расширения GetNext(). Ошибка:

Метод расширения должен быть определен в неэквивалентном статическом классе

Достаточно честный, поэтому я переношу его в свой собственный класс, но все же инкапсулированный внутри объекта, где он принадлежит:

class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static class Extensions
    {
        static int GetNext(this int i)
        {
            return i + 1;
        }
    }
}

Все еще нет кубиков. Теперь ошибка:

Метод расширения должен быть определен в статическом классе верхнего уровня; Расширения - это вложенный класс.

Есть ли веская причина для этого требования? Бывают случаи, когда метод-помощник действительно должен быть конфиденциально инкапсулирован, и бывают случаи, когда код намного чище и читабельнее/поддерживается, если вспомогательный метод является методом расширения. Для случаев, когда эти два пересекаются, могут быть выполнены или мы должны выбрать один над другим?

4b9b3361

Ответ 1

Я считаю, что лучшее, что вы можете получить в общем случае, - это класс internal static с методами расширения internal static. Поскольку это будет в вашей собственной сборке, единственные люди, которых необходимо предотвратить использование расширения, являются авторами сборки - поэтому некоторые явно названные пространства имен (например, My.Extensions.ForFoobarOnly) могут быть достаточными, чтобы намекнуть, чтобы избежать неправильного использования.

Минимальное ограничение internal, охватываемое реализовать расширение.

Класс должен быть видимым для кода клиента... с хотя бы той же видимостью, что и содержащий класс.

Примечание. Я бы сделал расширение public в любом случае, чтобы упростить модульное тестирование, но поместил в явное имя пространства имен, например Xxxx.Yyyy.Internal, чтобы другие пользователи сборки не ожидали, что методы будут поддерживаться/разрешаться. По существу полагайтесь на соглашение, отличное от компиляции времени.

Ответ 2

Есть ли веская причина для этого требования?

Это неправильный вопрос. Вопрос, заданный командой разработчиков языка при разработке этой функции, был:

Есть ли убедительная причина, по которой методы расширения могут быть объявлены во вложенных статических типах?

Так как методы расширения были разработаны, чтобы заставить LINQ работать, и у LINQ нет сценариев, где методы расширения были бы частными для типа, ответ был "нет, такой убедительной причины нет".

Устраняя возможность помещать методы расширения в статические вложенные типы, ни одно из правил поиска методов расширения в статических вложенных типах не нужно было придумывать, спорить, разрабатывать, указывать, реализовывать, тестировать, документировать, отправлять клиентам или совместим со всеми будущими функциями С#. Это была значительная экономия средств.

Ответ 3

Этот код компилируется и работает:

static class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}

Обратите внимание на строку static class Program, которая была написана компилятором.

Ответ 4

Я считаю, что они реализовали компиляцию методов расширений.

Глядя на IL, кажется, что они добавляют некоторые дополнительные атрибуты в метод.

.method public hidebysig static int32 GetNext(int32 i) cil managed
{
    .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] int32 num)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldc.i4.1 
    L_0003: add 
    L_0004: dup 
    L_0005: starg.s i
    L_0007: stloc.0 
    L_0008: br.s L_000a
    L_000a: ldloc.0 
    L_000b: ret 
}

Вероятно, существует очень фундаментальное значение, которое нам не хватает, что просто не заставляет его работать, поэтому ограничение существует. Также может быть, что они хотели заставить кодирование. К сожалению, он просто не работает и должен находиться в статических классах верхнего уровня.

Ответ 5

Это взято из примера на microsoft msdn. Методы Extesnion должны быть определены в статическом классе. Посмотрите, как класс Static был определен в другом пространстве имен и импортирован. Здесь вы можете увидеть пример http://msdn.microsoft.com/en-us/library/bb311042(v=vs.90).aspx

namespace TestingEXtensions
{
    using CustomExtensions;
    class Program
    {
        static void Main(string[] args)
        {
            var value = 0;
            Console.WriteLine(value.ToString()); //Test output
            value = value.GetNext(); 
            Console.WriteLine(value.ToString()); // see that it incremented
            Console.ReadLine();
        }
    }
}

namespace CustomExtensions 
{
    public static class IntExtensions
    {
        public static int GetNext(this int i)
        {
            return i + 1;
        }
    }
}