Могут ли быть частные методы расширения?
Скажем, мне нужен простой частный помощник, и интуитивно в коде он будет иметь смысл в качестве метода расширения. Есть ли способ инкапсулировать этого помощника в единственный класс, который действительно должен его использовать?
Например, я пробую это:
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;
}
}
}
Все еще нет кубиков. Теперь ошибка:
Метод расширения должен быть определен в статическом классе верхнего уровня; Расширения - это вложенный класс.
Есть ли веская причина для этого требования? Бывают случаи, когда метод-помощник действительно должен быть конфиденциально инкапсулирован, и бывают случаи, когда код намного чище и читабельнее/поддерживается, если вспомогательный метод является методом расширения. Для случаев, когда эти два пересекаются, могут быть выполнены или мы должны выбрать один над другим?
Ответы
Ответ 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;
}
}
}