It isn't cheating, but like any tool, it should be used for what it is intended to solve. Reflection, by definition, allows you to inspect and modify code through code; if that is what you need to do, then reflection is the tool for the job. Reflection is all about meta-code: Code that targets code (as opposed to regular code, which targets data).
An example of good reflection use is generic web service interface classes: A typical design is to separate protocol implementation from payload functionality. So then you have one class (let's call it
T) that implements your payload, and another that implements the protocol (
T is fairly straightforward: for every call you want to make, simply write one method that does whatever it is supposed to do.
P, however, needs to map web service calls to method calls. Making this mapping generic is desirable, because it avoids redundancy, and makes
P highly reusable. Reflection provides the means to inspect class
T at runtime and call its methods based on strings passed into
P through the web service protocol, without any compile-time knowledge of class
T. Using the 'code about code' rule, one can argue that class
P has the code in class
T as part of its data.
Reflection also gives you tools to get around restrictions of the language's type system - theoretically, you could pass all parameters as type
object, and call their methods through reflections. Voilà, the language that is supposed to enforce strong static typing discipline now behaves like a dynamically typed language with late binding, only that the syntax is far more elaborate. Every single instance of such a pattern I've seen so far has been a dirty hack, and invariably, a solution within the language's type system would have been possible, and it would have been safer, more elegant, and more efficient in all regards.
A few exceptions exist, such as GUI controls that can be data-bound to various non-related types of data sources; mandating that your data implement a certain interface just so you can data-bind it isn't realistic, and neither is having the programmer implement an adaptor for each type of data source. In this case, using reflection to detect the type of data source and adjusting the data binding is a more useful choice.