Вопрос или проблема
Я пишу юнит-тесты для методов расширения, определенных для типа перечисления. Это тест
namespace ABC.ACSUnitySDKTest
{
[TestFixture]
public class AudioStreamSampleRateExTests
{
[Test]
public void ToHz_16000Hz_ReturnsCorrectValue()
{
var result = AudioStreamSampleRate.Hz_16000.ToHz(); // <-- ОШИБКА! .ToHz() не определен!
Assert.That(result, Is.EqualTo(16000));
}
}
}
Полное сообщение об ошибке:
CS1061 ‘AudioStreamSampleRate’ не содержит определения для
‘ToHz’ и не найдено доступного метода расширения ‘ToHz’, принимающего первый
аргумент типа ‘AudioStreamSampleRate’ (не хватает директивы using или ссылки на сборку?)
Но метод расширения определен, и компилятор C# уже должен это видеть:
namespace ABC
{
internal static class AudioStreamSampleRateEx
{
public static int ToHz(this AudioStreamSampleRate sampleRate)
{
return sampleRate switch
{
AudioStreamSampleRate.Hz_16000 => 16000,
AudioStreamSampleRate.Hz_22050 => 22050,
AudioStreamSampleRate.Hz_24000 => 24000,
AudioStreamSampleRate.Hz_32000 => 32000,
AudioStreamSampleRate.Hz_44100 => 44100,
AudioStreamSampleRate.Hz_48000 => 48000,
_ => throw new NotImplementedException()
};
}
}
}
Так почему компилятор C# не может увидеть метод расширения .ToHz()?
Примечание: Нам не нужно добавлять директиву ‘using’, потому что они находятся в одном пространстве имен. В любом случае, я добавил директиву ‘using’, и это не решило проблему.
Я понял, почему компилятор C# не может видеть метод расширения. Модификатор доступа internal в классе метода расширения AudioStreamSampleRateEx не позволяет этому классу и, следовательно, методу расширения быть доступными в тестовом классе AudioStreamSampleRateExTests.
Чтобы исправить это, я добавил этот атрибут:
[assembly: InternalsVisibleTo("ACSUnitySDKTest")]
в верхней части файла, в котором был определен класс ‘internal’. Этот атрибут делает внутренние типы и члены сборки, в которой этот атрибут объявлен, видимыми для другой сборки, в данном случае для сборки “ACSUnitySDKTest”.
Проблема решена!
Обновление: Другие упомянули, что я могу заменить модификатор доступа ‘internal’ на ‘public’. Да, это может решить проблему, но использование ‘internal’ является дизайнерским решением, принятым другими, и я не должен его изменять.
Ответ или решение
Для решения проблемы с доступом к методу расширения, определенному для типа перечисления (enum
), необходимо учитывать особенности доступа к внутренним (internal) членам классов в C#. Давайте подробно разберем суть проблемы и предложенные решения.
Проблема: Ошибка компиляции при использовании метода расширения
При написании юнит-тестов для методов расширения, определенных для перечисления AudioStreamSampleRate
, возникает ошибка компиляции:
CS1061 'AudioStreamSampleRate' does not contain a definition for 'ToHz' and no accessible extension method 'ToHz' accepting a first argument of type 'AudioStreamSampleRate' could be found (are you missing a using directive or an assembly reference?)
Причиной этой ошибки является то, что метод расширения объявлен в классе с модификатором доступа internal
, что ограничивает его видимость только в пределах текущей сборки. Ваша тестовая сборка, ACSUnitySDKTest
, не имеет доступа к методам и типам, объявленным как internal
в другой сборке.
Решение: Использование InternalsVisibleTo
Для того чтобы сделать методы и типы internal
видимыми в других сборках, можно использовать атрибут InternalsVisibleTo
. Этот атрибут позволяет явно указать, какие другие сборки могут видеть ваши внутренние члены. В вашем случае, чтобы разрешить доступ к методу ToHz
, необходимо добавить следующий код в файл, содержащий определение класса:
[assembly: InternalsVisibleTo("ACSUnitySDKTest")]
Это позволит сборке ACSUnitySDKTest
видеть все внутренние члены сборки, где объявлен класс AudioStreamSampleRateEx
.
Альтернативное решение: Изменение модификатора доступа
Другим вариантом, чтобы решить проблему, является изменение модификатора доступа с internal
на public
. Однако, как вы упомянули, это может быть нежелательным в силу существующих архитектурных решений, и такие изменения могут быть подвержены критике со стороны команды.
Заключение
В итоге, использование атрибута InternalsVisibleTo
является оптимальным решением для вашей ситуации. Оно позволяет сохранить модификатор internal
, что может быть важным для поддержания инкапсуляции и безопасности вашего кода. Таким образом, вы сможете тестировать внутренние члены без необходимости их публичного объявления.
Пользуясь этим подходом, вы сможете избежать ошибочных пониманий, связанных с доступом к методам расширения, и обеспечить целостность дизайна вашего приложения, следуя лучшим практикам программирования в C#.