1: звучит жизнеспособно; часто это субъективный вызов; например, если ваша утилита опирается на статические поля, это ограничивает вас одной установкой на домен приложения. Это может быть хорошо, но может быть ограничением, если вы позже перейдете на многопользовательский режим. Это также может быть сложнее проверить.
2: статический класс не может иметь экземпляры (или методы экземпляров); если все методы реализованы как статические, то, вероятно, это должен быть статический класс
3: Я не вижу здесь преимущества синглтона по сравнению со статическим. Одиночка полезна, если вам нужно рассматривать ее как пример, например, для реализации интерфейса.
Другим вариантом здесь может быть обычный экземпляр, но просто убедитесь, что весь ваш код взаимодействует с одним и тем же экземпляром - возможно, через IoC / DI (и, возможно, нет). Это даст вам аналогичное удобство, но больше гибкости для тестирования и мультитенантности
В качестве примечания вы также можете рассмотреть последствия threading , особенно в веб-приложении (с высокой степенью многопоточности). Общие данные (включая статические поля и общие экземпляры) должны быть правильно синхронизированы (или неизменны).