Inconsistant null parameter handling in Reflection

S

Stefan Hong

Hi,

It seems that the way reflection resolves methods is not quite the same as
default CLR. For example this simple class:

class Test {
public void Hello(string name) {
Console.WriteLine("Hello(string)");
}
public void Hello(object obj) {
Console.WriteLine("Hello(object)");
}
}

If I invoke the method normally using null parameter:

Test test = new Test();
test.Hello(null); // shows Hello(string)

But if I only know the input parameter (which is null) and the name of the
method, and trying to use reflection to resolve the method to invoke, it
becomes:

Test test = new Test();
MethodInfo method = test.GetType().GetMethod("Hello", new Type[] {
typeof(void) });
method.invoke(test, new object[] { null }); // shows Hello(object)

The result changed. It must because of the typeof(void) thing, but I can't
say typeof(string) because the input parameter is null. What should I do to
get the same result?


Thanks,
Stefan
 
J

Jon Skeet [C# MVP]

Stefan Hong said:
It seems that the way reflection resolves methods is not quite the same as
default CLR.

It's not the CLR that decides which overload to call in the statically
compiled case - it's the C# compiler.

But if I only know the input parameter (which is null) and the name of the
method, and trying to use reflection to resolve the method to invoke, it
becomes:

Test test = new Test();
MethodInfo method = test.GetType().GetMethod("Hello", new Type[] {
typeof(void) });
method.invoke(test, new object[] { null }); // shows Hello(object)

The result changed. It must because of the typeof(void) thing, but I can't
say typeof(string) because the input parameter is null. What should I do to
get the same result?

You can still use typeof(string) even if the input parameter is null.
(I'm surprised you can get away with typeof(void) in the above, to be
honest.)

If you only receive null and you basically don't know which method to
call, but you want to call the most specific method, you'll need to
call Type.GetMethods and work out which one to call for yourself.
 
S

Stefan Hong

Jon Skeet said:
It's not the CLR that decides which overload to call in the statically
compiled case - it's the C# compiler.

You can still use typeof(string) even if the input parameter is null.
(I'm surprised you can get away with typeof(void) in the above, to be
honest.)

If you only receive null and you basically don't know which method to
call, but you want to call the most specific method, you'll need to
call Type.GetMethods and work out which one to call for yourself.

Hmm... to completely mimic the type binding rule used by C# compiler seems
not easy and error-prone, is there a such Binder class exists in the .net
framework library already?


Thanks,
Stefan
 
J

Jon Skeet [C# MVP]

Stefan Hong said:
Hmm... to completely mimic the type binding rule used by C# compiler seems
not easy and error-prone, is there a such Binder class exists in the .net
framework library already?

I don't believe so, I'm afraid. I would try to avoid it, personally,
but it's hard to know exactly what that entails without having more
information about your problem.
 
R

Robert Jordan

Hi,
Test test = new Test();
MethodInfo method = test.GetType().GetMethod("Hello", new Type[] {
typeof(void) });
method.invoke(test, new object[] { null }); // shows Hello(object)

You should try

test.GetType().InvokeMember(
"Hello", BindingFlags.Public, null, test, new object[] {null}
);

The default binder will be applied and I'd bet, that it
will dispatch to the correct method.

bye
Rob
 
S

Stefan Hong

Jon Skeet said:
I don't believe so, I'm afraid. I would try to avoid it, personally,
but it's hard to know exactly what that entails without having more
information about your problem.

I am experimenting a new remote invocation protocol, similar to what web
services can do but is a completely different thing. I can unwrap the
invocation request at the remote site to get the name of the method and an
array of unmarshalled parameter objects, but then I can not find the right
method to invoke, especially when there is null among input parameters.


Stefan
 
S

Stefan Hong

Robert Jordan said:
Hi,
You should try

test.GetType().InvokeMember(
"Hello", BindingFlags.Public, null, test, new object[] {null}
);

The default binder will be applied and I'd bet, that it
will dispatch to the correct method.

I tried do this:

test.GetType().InvokeMember(
"Hello",
BindingFlags.Public | BindingFlags.InvokeMethod,
null, test, new object[] {null});

But I got a MissingMethodException says:

"Attempted to access a missing member"

Any idea?


Thanks,
Stefan
 
S

Stefan Hong

Stefan Hong said:
I tried do this:

test.GetType().InvokeMember(
"Hello",
BindingFlags.Public | BindingFlags.InvokeMethod,
null, test, new object[] {null});

But I got a MissingMethodException says:

"Attempted to access a missing member"

Any idea?

I've played with the BindingFlags and now I can invoke the correct method
with this:

test.GetType().InvokeMember(
"Hello",
BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
null, test, new object[] {null});

I tried to apply the same thing to test.GetType().GetMethod( ... ) but
wasn't able to get the same result. I must get MethodInfo before invoking
'cause I want to check some custom attributes first.

This could be a design flaw of Reflection because there is no way to specify
the type of null reference. I thought typeof(void) could do the trick but
it couldn't. Maybe we need something like typeof(null) supported by C#
compiler or Type.Null in the framework.


Stefan
 
J

Jon Skeet [C# MVP]

Stefan Hong said:
I've played with the BindingFlags and now I can invoke the correct method
with this:

test.GetType().InvokeMember(
"Hello",
BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
null, test, new object[] {null});

I tried to apply the same thing to test.GetType().GetMethod( ... ) but
wasn't able to get the same result. I must get MethodInfo before invoking
'cause I want to check some custom attributes first.

This could be a design flaw of Reflection because there is no way to specify
the type of null reference. I thought typeof(void) could do the trick but
it couldn't. Maybe we need something like typeof(null) supported by C#
compiler or Type.Null in the framework.

No, because you don't *want* a typeof(null) or typeof(void). You want
to say what the declared parameter type is - eg typeof(string).
 
S

Stefan Hong

Jon Skeet said:
No, because you don't *want* a typeof(null) or typeof(void). You want
to say what the declared parameter type is - eg typeof(string).

If I already know the declared parameter type is string, I would definitely
use typeof(string). But in my case I don't. All I know is the name of the
method and the parameter objects. Default binder can resolve method without
any problem, so we can do:

test.Hello( null );

The null is just nothing, no declared type. Binder knows how to escalate
null to the most specific type. The problem is when we want to resolve the
MethodInfo using the same binding rule, there is no way to tell binder that
we have a null parameter to let binder do the escalation magic. That's why
I think a pseudo Type.Null or typeof(null) might be necessary.


Stefan
 
J

Jon Skeet [C# MVP]

Stefan Hong said:
If I already know the declared parameter type is string, I would definitely
use typeof(string). But in my case I don't. All I know is the name of the
method and the parameter objects. Default binder can resolve method without
any problem, so we can do:

test.Hello( null );

The null is just nothing, no declared type. Binder knows how to escalate
null to the most specific type. The problem is when we want to resolve the
MethodInfo using the same binding rule, there is no way to tell binder that
we have a null parameter to let binder do the escalation magic. That's why
I think a pseudo Type.Null or typeof(null) might be necessary.

In the above, it's not the Binder at all - it's the C# overload
resolution rules. Type.Null or typeof(null) wouldn't help at all,
because there's no such thing as the null type - and certainly no
methods would be matching it. I think I see what you mean though.

I've tried getting Type.DefaultBinder.BindToMethod to work, but I
haven't managed so far...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top