Perhaps you are missing the whole idea of reflection. And the type cast. Also you need to understand that in OOP there are
compile-time and
runtime types.
What is type cast? In case of reference type, including a
boxed type, you don't change the object itself at all. Normally, you up-cast it; that is, you inform the compiler that you consider the
compile-time type of the object as some
statically known type. This is only the assumption based on your knowledge of other parts of your software. During execution, it may so happen that the
runtime type of the object appear
assignment-compatible with your assumed type. If you did everything correctly, you will get access to statically known members of the target type of the cast, otherwise a cast exception will be thrown. This exception protects you from the trouble of "accessing" the
instance members of an object which doesn't actually have it.
Such type cast goes against normal OOP technology, but so does reflection, so we understand that sometimes it's necessary to do so.
So, it's all about having this statically known type, the target type of the case, and hence its members. Can it be the same type as the runtime type of your object? Now, we are coming to the key point:
if you construct an object through reflection, you don't have this type, otherwise you should not use reflection. You only have the instance of the
System.Type
, which is not the type in the sense known to a compiler; it only appears during runtime, that is, after the build is already done; you cannot use it.
Indeed, if you knew the statically known type which is supposed to be the same as the runtime type of the object you want to construct, you could just do it using a constructor, without any type cast. It is possible when this type is compiled in any of the assembles statically referenced in your application, or the application assembly itself. It also means that this assembly is known to your project.
But the whole reason to use reflection is the situation when you don't have this assembly before runtime. Usually, you load the assembly using one of the methods of
System.Assembly
, such as
Load/LoadFrom
. For future considerations, I'll call this dynamically loaded assembly a
plug-in assembly, and the assembly loading another assembly can be called
host assembly. This naming means nothing; this is just the most common semantic interpretation; I'll use it just for convenience.
In host assembly, during compile time, your compiler remains
agnostic of the content of any of the plug-in assemblies. So, what to do? You have to continue using reflection. Say, you need to use some method you assume to be there. Using reflection, you obtain the instance of
MethodInfo
. You can get it by name, or list all methods and choose one with expected set of arguments, and so on. Then you call one of its
Invoke
methods. This is your call:
MethodInfo Class (System.Reflection)[
^],
MethodBase.Invoke Method (Object, BindingFlags, Binder, Object[], CultureInfo) (System.Reflection)[
^],
MethodBase.Invoke Method (Object, Object[]) (System.Reflection)[
^].
Note the first argument of the two
Invoke
methods, the argument
object
. This is the reference you obtained when you created the object using reflection, the target object itself. But you need it only to call an
instance method. For a static method, you don't need this object; and the reflection is done based on the instance of
System.Type
along. This object is actually the "this" argument passed indirectly to instance method in a "normal" method call. Please see my past answers:
C# windows base this key word related and its uses in the application[
^],
Type casting in c#[
^].
By the way, the most general method of instantiation of the object (which you performed using
Activator
) is invocation of a constructor, which is similar to the invocation of a static method. You can choose one of the available constructors (at least one is always available in all non-abstract complete types):
ConstructorInfo Class (System.Reflection)[
^] (see also all
Invoke
methods).
More importantly,
the whole idea of getting some types and some type members by names is counter-productive. What happens is you simply rename some types and some methods? The whole application will stop working, but it won't be detected during your build. This is not maintainable.
One stable approach is to use some interface statically known by both host assembly and plug-in assemblies. Some types in plug-in assemblies can implement it (and hence, these types can also be value-type
struct
, not only
class
). The host assembly can recognize/confirm that some types in some plug-in assemblies implement this interface and instantiate some object of these type using reflection. And then you can cast your instance to this interface type. That's all. I explained it in my past answers:
Gathering types from assemblies by it's string representation[
^],
C# Reflection InvokeMember on existing instance[
^],
Dynamically Load User Controls[
^],
Access a custom object that resides in plug in dll[
^].
—SA