Как реализовать метод, вызывающий нужный сеттер по параметрам, если сеттеры находятся в разных сборках и не публичны?
Что проверяют
Знаете ли вы рефлексию .NET на практике и понимаете ли её цену. Сильный кандидат сначала спрашивает, зачем это нужно: обход модификаторов доступа почти всегда говорит о проблеме в дизайне. Следом спрашивают про производительность и про то, что станет с таким кодом при trimming и Native AOT.
Как отвечать
Начните с легального варианта. Если сборки ваши и сеттеры internal, достаточно [assembly: InternalsVisibleTo("Other.Assembly")]. Если так нельзя, остаётся рефлексия:
- тип:
Type.GetType("Ns.Target, Other.Assembly")по имени с указанием сборки, если она уже загружена или лежит рядом. Иначе загрузка черезAssemblyLoadContext; - выбор по параметрам:
GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)и фильтр по имени иPropertyType.IsAssignableFrom(value.GetType()). Перегрузки метода-сеттера различают черезGetMethod(name, flags, null, new[] { typeof(int) }, null), иначе при нескольких перегрузках будетAmbiguousMatchException; - вызов:
prop.GetSetMethod(nonPublic: true)вернёт даже приватный аксессор, дальшеInvoke. Исключение из самого сеттера придёт обёрнутым вTargetInvocationException, если не передатьBindingFlags.DoNotWrapExceptions; - скорость:
MethodInfoкешируют по ключу «тип, имя», а для частых вызовов строят делегатsetter.CreateDelegate<Action<Target, int>>()или компилируют дерево выражений.
С .NET 8 есть [UnsafeAccessor]: объявляете static extern метод с UnsafeAccessorKind.Method и Name = "set_Value", и рантайм связывает его с приватным сеттером без рефлексии. Это быстро и совместимо с AOT, но тип цели нужно знать на этапе компиляции.
Где спотыкаются
Ищут приватный сеттер свойства базового класса через тип наследника. Приватные члены базы так не видны, нужно подниматься по BaseType или брать DeclaringType.
Вызывают GetMethod на каждый вызов. Поиск по метаданным дороже самого вызова, и в цикле это сразу видно в профилировщике.
Забывают про trimming. При публикации с обрезкой члены, к которым обращаются только рефлексией по строке, могут быть удалены, и нужна разметка [DynamicallyAccessedMembers].