Если это вообще возможно, мы хотим написать безопасный для типов код, в котором компилятор сообщает нам, действительно ли мы вызываем метод, который действительно существует, передаем ли мы правильный аргумент и т. Д. Таким образом, если что-то не так код даже не скомпилируется, и мы видим проблему еще до того, как попробуем запустить наш код.
Глядя на код с отражением, вот некоторые вещи, которые могут пойти не так. В каждом случае, если бы мы использовали ссылочные сборки и строго типизированные объекты и методы, компилятор поймал бы это и предупредил нас. Используя отражение, код компилируется, но мы не узнаем о проблемах, пока не запустим программу:
// The assembly might not be there. Or we can't load it.
Assembly assembly = Assembly.LoadFile(@"C:\Users\myUser\source\repos\TestMathHelper\TestMathHelper\bin\Debug\MathHelper.dll");
// The assembly doesn't have a type with that name.
Type type = assembly.GetType("MathHelper.Helper");
// The type doesn't have a constructor with no arguments.
object instance = Activator.CreateInstance(type);
// The type doesn't have a method called "add".
MethodInfo method = type.GetMethod("add");
// The "add" method doesn't take two ints as arguments or doesn't return an int.
int result = (int)method.Invoke(instance, new object[] {4, 5});
Не говоря уже о том, что для нас или разработчика, который придет дальше, возможность набрать имя класса и предложить нашей IDE предложить имена методов и свойств - это здорово. В противном случае нам нужно было бы каждый раз просматривать исходный код или документацию для другого класса, чтобы увидеть, каковы его члены, какие аргументы он принимает, что эти аргументы значат и что он возвращает.
Отражение обычно лучше в тех случаях, когда конкретный тип неизвестен и не так важен.
Например, мы могли бы написать метод, который принимает List<T>
, где T
- некоторый объект, а затем он читает все открытые свойства каждого экземпляра и записывает их все в файл CSV.
public void WriteToCsv<T>(IEnumerable<T> items, StreamWriter writer)
В этом случае мы не пишем метод для обработки определенного типа. Нас не волнует, что это за тип. Каким бы ни был T
, мы бы использовали рефлексию, чтобы выяснить, каковы его общедоступные свойства. Затем мы будем использовать эти свойства, чтобы получить соответствующие значения для каждого элемента.
Это чрезмерное обобщение. Есть много действительных случаев использования рефлексии. Чего нам следует избегать, так это использовать его, когда мы имеем дело с известными типами, и есть способ сделать то, что мы хотим, не задумываясь. Если мы рисуем себя в углу, и похоже, что нам нужно использовать отражение, то, возможно, мы сможем исправить часть дизайна. Или, если мы думаем, что нам нужно отражение, мы можем взглянуть на это с другой стороны. Я говорю это только потому, что хотя рефлексия полезна, она используется во многих случаях, когда она не нужна и делает код более хрупким и трудным для понимания.