Первый вариант довольно обычный и имеет преимущество простоты. Вы правы, говоря, что он немного подвержен рефакторингу, но ... это редко "настоящая" проблема.
Другой (четвертый) подход, однако, заключался бы в том, чтобы сделать атрибут абстрактным с помощью абстрактного метода и создать для него подкласс с кодом преобразования в атрибуте - тогда во время выполнения вы можете получить атрибут (как базовый атрибут) и просто вызовите виртуальный метод, этот подход распространен в таких вещах, как MVC.
Лично я обычно просто использую первый вариант вместе с юнит-тестами на безопасность (в случае рефакторинга и т. Д.).