Click here to Skip to main content
15,902,112 members
Please Sign up or sign in to vote.
2.00/5 (1 vote)
See more:
Hi,

I'm writing a method, which's task is to raise the proper events with custom event args when it is called. So, it looks like:

C#
string asdf = "EventReflectionTester." + eventToRaise + "EventArgs";

            Type argType = Type.GetType(asdf);
                       
            var vs = new ValueSet();
            vs["IP"] = "valami";
            vs["Port"] = 123;

            var obj = Activator.CreateInstance(argType, vs);
            var o1 = Convert.ChangeType(obj, argType);


Than I need to raise the event like this:
C#
ev.Raise(eventToRaise, (CUSTOM_EVENTARG_TYPE)eventArg);

o1's type is my CUSTOM_EVENTARG_TYPE at runtime, but at build it is object, so I can't use it in ev.Raise(). How can I cast it and pass to ev.Raise()?

Thanks in advance.
Posted
Comments
Sascha Lefèvre 2-Feb-16 9:53am    
I assume ev.Raise() is a generic method? Can you show the code for it?
Sergey Alexandrovich Kryukov 2-Feb-16 10:38am    
It says nothing about it, but this is not a real problem. Let's say, this is just a known/expected method. The problem is the problem of having a statically known target type of the type cast, otherwise the method should also be obtained via reflection.

Please see Solution 1 where I explain it all, including a more acceptable alternative.

—SA
dtoth2 2-Feb-16 12:29pm    
Yes, it is a generic method. I can't show the method's body, because it is business code, but the the header looks like
public static void Raise<teventargs>(this object source, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs
{
...
}
Sergey Alexandrovich Kryukov 2-Feb-16 12:36pm    
I did not cover generic methods in my answer, because I did not know from your question what it is. I can cover it if you want, but first read and understand my answer.

The idea is: before calling a generic method, you perform instantiation of generic into a complete method. With the approach based on the interface, it won't be a problem at all, so you may not need any additional explanations. In case you will, please ask me some follow-up questions.

—SA

1 solution

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
 
Share this answer
 
v5
Comments
CPallini 2-Feb-16 12:06pm    
5.
Sergey Alexandrovich Kryukov 2-Feb-16 12:32pm    
Thank you, Carlo.
—SA
dtoth2 2-Feb-16 12:40pm    
Thanks, it is a really nice explanation :) Yes, reflection must be used carefully, but it is necessary for me in this situation. The provided example is working if I'm not using reflected EventArgs types, but in that way I need to write a switch-case for example for every type of EventArgs. So the real question is: how can I tell to the system at runtime to use my o1 object as Type? I only need to make a cast like
int foo = (int)customEventarg.Data;
Is there any possibility to make this beside of the interface declaring?
Sergey Alexandrovich Kryukov 2-Feb-16 12:51pm    
You are welcome.

I hear you. Necessary is necessary. No, why switch-case? I answered your "real question" in my last two paragraphs of my answer. Why "besides of the interface declaring"? You cannot detect a runtime type by definition, because it is always not any of the types you have defined in your statically known assemblies. The idea just makes no sense. Well, instead of interface you can use some base type (typically, abstract class), but it won't make a lot of difference. Interface is just the most flexible thing because 1) it allows multiple inheritance, 2) can be implemented by struct, not only class. It is clearer, too.

Well, you can work without type cast at all, and I explained it above those two paragraphs. You can consider it as a solution "beside of the interface declaring".

Essentially, I gave you all the options, only some detail may very. Are you ready to accept the answer formally?

In all cases, your follow-up questions will be welcome.
dtoth2 2-Feb-16 12:59pm    
okay, will try it with interfaces. Anyway, it is a good answer, will accept it :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900