Why would RuntimeBinderException occur at runtime calling an overloaded function? - c#

I've finally produced a minimal example that reproduces this error:
using System;
using Newtonsoft.Json;
class Program
{
public byte[] Foo(byte[] p) { return new byte[0]; }
public byte[] Foo(Guid? p) { return new byte[0]; }
static Guid? ToGuid(string s) { return s == null ? null : (Guid?)new Guid(s); }
void Bar()
{
dynamic d = JsonConvert.DeserializeObject<dynamic>("{}");
var id = d?.id?.ToString();
Foo(ToGuid(id));
}
static void Main(string[] args)
{
new Program().Bar();
}
}
Bizarrely it's crashing at runtime calling Foo when d.id is null (or not a string), saying it can't resolve which version of Foo to call (The call is ambiguous between the following methods or properties). Why on earth isn't this resolved at compile time though? The dynamic shouldn't make a difference that I can see, and in fact even more weirdly if I add an explicit cast "(Guid?)" before ToGuid... it works as expected, and likewise if I instead write it as:
Guid? id = ToGuid(d.id?.ToString());
Foo(id)
which actually makes more sense anyway. It also works fine if I change "var" to "string".
I noticed that the exception is initially thrown from "System.Linq.Expressions.dll" which is a bit odd. The full stack trace is basically:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The call is ambiguous between the following methods or properties: 'FooService.Foo(byte[])' and 'FooService.Foo(System.Guid?)'
at CallSite.Target(Closure , CallSite , FooService , Object )
Exception source is "Anonymously Hosted DynamicMethods Assembly"

Now that we have the var variant I can reproduce the issue. And the issue is nulls. You may think that the return type of ToGuid must be a Guid? because you're assuming knowledge that the compiler doesn't work with. So far as it's concerned, in Bar it's looking at an id with type dynamic1. This means that it's going to assume that whatever ToGuid returns it'll store in a dynamic temp variable.
In this case, it returns null and under the covers, dynamic is just object. At that point, we've lost any compile time type information about the return type from ToGuid. If it wasn't null, before it resolves Foo it would effectively call GetType on the instance. But that's not possible here. It has null and two equally good/bad candidates it could call with a null reference. It's as if you'd written Foo(null); (which generates the equivalent error at compile time).
Inserting an explicit cast - Foo((Guid?)ToGuid(id)); gives the runtime sufficient information at the callsite to be able to unambiguously select the correct overload of Foo you wanted it to choose.
1Bear in mind that whatever the type of the id property on d is, it may have a ToString method that shadows the one from object. It cannot assume it returns a string so id is dynamic too.

Related

Aggregate: Seed with a null value [duplicate]

private static Matcher<T> EqualTo<T>(T item)
{
return new IsEqual<T>(item);
}
How do I modify the above method definition such that the following are valid/allowed.
EqualTo("abc");
EqualTo(4);
EqualTo(null); // doesn't compile. EqualTo<string>(null) does
Trying to port some Java code where null seems to be acceptable value for a T parameter.
Update
Thanks: for all the answers - especially Eamon and Jason. I didn't want the method calls to bother with type-inference. The following overload fixed it.
private static Matcher<object> EqualTo(object item)
{
return EqualTo<object>(item);
}
Actually the above question was a part of a larger puzzle. The end goal was for the following to work.
this.AssertThat(null, EqualTo(null));
this.AssertThat(null, Not(EqualTo("hi")));
this.AssertThat("hi", Not(EqualTo(null)));
Applied the same fix.. RFC. (Ignore the ugly extension method part - that's another problem. Wanted to have these methods in all test-fixtures without inheritance.)
public static void AssertThat<T>(this object testFixture, object actual, Matcher<T> matcher, string message = "")
{
AssertThat(anyObject, (T)actual, matcher, message);
}
public static void AssertThat<T, TSuper>(this object testFixture, T actual, Matcher<TSuper> matcher, string message = "") where T : TSuper
{
... check and assert
Consider the following method:
public bool IsNullString<T>(T item) {
return typeof(T) == typeof(string) && item == null;
}
Yes, this is a pathetically stupid method and using generics is pointless here, but you'll see the point in a moment.
Now consider
bool first = IsNullString<string>(null);
bool second = IsNullString<Foo>(null);
bool third = IsNullString(null);
In the first and second, the compiler can clearly distinguish the type of T (no inference is needed). In the third, how the compiler infer what T is? In particular, it can't distinguish between T == string and T == Foo, or any other type for that matter. Therefore, the compiler has to give you a compile-time error.
If you want to get around this, you either need to cast null
EqualTo((object)null);
or explicitly state the type
EqualTo<object>(null)
or define an overload
private static Matcher<object> EqualTo(object item) {
return new IsEqual<object>(item);
}
Not possible without explicitly specifying a T or doing a cast. Generics are compile time constructs and as such if the compiler can't figure out the type at compile time, then it won't compile (as you're seeing).
Since you can't do exactly what you are wanting to do, how about defining an EqualTo(object) overloaded method? That should allow your required syntax.
You may work around this limitation by using the following syntax:
EqualTo("abc");
EqualTo(4);
EqualTo(default(object));
//equivalently:
EqualTo((object)null);
default(T) is the value a field of type T has if not set. For reference types, it's null, for value types it's essentially memory filled with zero bytes (...which may mean different things for different types, but generally means some version of zero).
I try to avoid the null everywhere in my code nowadays. It hampers type inference elsewhere too, such as with the var declared field and in a ternary operator. For example, myArray==null ? default(int?) : myArray.Length is OK, but myArray==null ? null : myArray.Length won't compile.
Maybe implementing a non-generic EqualTo, which takes an Object as the argument type, would solve the issue of rewriting those code lines.

C# compiler type inference with dynamic function parameters

When I call a function and replace one of the parameters with dynamic, the compiler inferres the function result to be dynamic. I don't understand why this happens.
Example: the inferred type for a is dynamic, so this code compiles, but of course fails at runtime with RuntimeBinderException:
dynamic b = "";
var a = MethodWithoutOverloads("", b);
a.DoesNotExist();
...
public string MethodWithoutOverloads(string a, string b) { ... }
Somebody knows why the type inferred is not the return type of the function?
EDIT: edited to make clear this happens with methods without overloads
You are right in the sense that the compiler could reason out that all String.Format overloads return a string and therefore infer that a must be a string no matter what b really is.
The truth is that the compiler does not do that. It solves the general case, which is good, and because overloads with different return types are valid in C#, it simply assigns the return type as dynamic and lets the runtime figure it out.
Answering your specific question,
public string MethodWithoutOverloads(string a, string b) { ... }
dynamic a = "";
var result = MethodWithoutOverloads(a, a); // result is dynamic.
Lets imagine the compiler decides that result is string and you publish to the wild west your library. Then, later on, you decide to add an overload with the following signature:
public int MethodWithoutOverloads(int a, int b) { ... }
Now, what should the type of result be? And, what happens to existing code that relied on result being strongly typed to string?
string result = MethodWithoutOverloads(someDynamicVariable, someOtherDynamicVariable);
The semantics change completely; before a consumer had a safe strongly typed variable, now he suddenly has a potentially unsafe implicit cast that can blow up in runtime.
Because the compiler doesn't know which method is going to be invoked at run time.
For example, you may have the two methods:
int MyMethod(int a)
{
return 5;
}
double MyMethod(string a)
{
return 6.0;
}
And you write the following code:
dynamic myThing = 5;
var myResult = MyMethod(myThing);
Considering we've explicitly said myThing is dynamic, and this its type is to be determined at runtime, we have no idea which method will be invoked (if any). Thus, we don't know the return type, either.

Why does c# compiler allow incorrect match of return value type and variable type?

Consider the following code:
static void Main()
{
dynamic a = 1;
int b = OneMethod(a);
}
private static string OneMethod(int number)
{
return "";
}
Please notice that type of b and return type of OneMethod does not match. Nevertheless it builds and throws the exception at runtime. My question is that why does the compiler let this? Or what is the philosophy behind this?
The reason behind this may be Compiler does not know which OneMethod would be called, because a is dynamic. But why it cannot see that there is only one OneMethod. There will surely be an exception at runtime.
Any expression that has an operand of type dynamic will have a type of dynamic itself.
Thus your expression OneMethod(a) returns an object that's typed dynamically
so the first part of your code is equivalent to
static void Main()
{
dynamic a = 1;
dynamic temp = OneMethod(a);
int b = temp;
}
one way of argue why this is sensible even in your case depends on whether or not you think the compiler should change behavior for that particular line depending when you add the below method
private static T OneMethod<T>(T number)
Now the compiler won't know the type returned until runtime. It won't even know which method is called. The generic or the non generic. Wouldn't you be surprised if it in the first case marked the assignment as a compile error and then by adding a completely different method it got moved to a runtime error?

Type of null literal in C#

I have a query about type of null.
I have a small program can anyone tell me about this.
public class TestApplication
{
public void ShowText(object ob)
{
Console.Write("Inside object");
}
public void ShowText(string str)
{
Console.Write("Inside string");
}
public void ShowText(int i)
{
Console.Write("Inside int.");
}
public void ShowText(char c)
{
Console.Write("Inside Character");
}
static void Main(string[] args)
{
new TestApplication().ShowText(null);
Console.Read();
}
}
Why it call the string function.
Is it means the type of null is string.
It might look a foolish conclusion but I am not able to find the region why it is calling the function of string.
Your question about the type of the null literal is answered here: What is the type of null literal?
But that doesn't really matter when talking about overload resolution. The actual null value itself will automatically be converted to whatever type it ends up as.
As for why the string overload is called:
You can't pass null as int and char parameters as they're value types, so those two overloads are out. (They could have been candidates had you made them nullable int? and char? types, but I won't go into that.)
Between the two other overloads taking reference types, string is a more specific type than object. That is, it derives from (and is therefore implicitly convertible to) object. Further, from the following section in the C# language spec (emphasis mine):
7.5.3.5 Better conversion target
Given two different types T1 and T2, T1 is a better conversion target than T2 if at least one of the following holds:
An implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists
...
Thus, the string overload is chosen as the best fit.
null can fit to any reference-type. and hence is a very good example for polymorphism.
lets say you have a Vehicle class. you create three more class Two-Wheeler, Three-Wheeler, Four-Wheeler.
if latter 3 class extends Vehicle class; it is called Vehicle is specified into those three categories.
So, given a value, say car, more specific fit to this value is Four-Wheeler.
Similar is the case with null. it can fit to either Object OR String; but more specific match is String.
Lets see how compiler thinks (specific to your code only) when it sees a call to ShowText(null) method
Find ShowText method.
Oh, I've found 4, methods, which one to call?!!
Lets see what argument is passed, hmm .. it's null.
Find ShowText method which has reference type as argument. all overloads with primitive arguments are ignored.
Oh No ... i can match it to eithe string OR object.
Which is more specific. i.e which comes first in inheritance hierarchy from below.
Hurray .. found it .. it's string .. lets call it.
And, for an exercise if you want to undestand that what will happen if compiler finds at step 6 mor than one matches ... define a ShowText(string[] vals) and see yourself.
This is an example of the overload resolution in C#, it does not prove that the type of the null is string.
null is a special value that is included in the domain of any reference type, it basically determines that there is not a valid reference. As such, it can be a string, an int?, an object etc, etc.
You can even cast null to any reference type, to get a "correctly" typed null, e.g. you must cast null if it's used as a value in a ? operator
int? value = (i > 0) ? i : null; // does not compile
int? value = (i > 0) ? i : (int?)null; //works

Can't write string from table to string attribute in C#

I read values from an local Access mdb-file. One value is stored as string in the db and I have it in a table. When using the GetType() method it return "System.String" and I can print it on the console without a problem but when I want to use it as an attribute for another method (requires a string) I get an error ("Cannot convert from 'object' to 'string'" and the same for 'int'). The same problems occur with some int values.
Am I doing something wrong or what is the problem in that case?
Console.WriteLine(dt.Rows[0][ProjName]); //prints project_name
Console.WriteLine(dt.Rows[0][ProjName].GetType()); //print "System.String"
Project = new Project(dt.Rows[0][ProjName], dt.Rows[0][MinDay], dt.Rows[0][MinWeek], dt.Rows[0][DayWeek]); //Error
Project = new Project(Convert.ToString(dt.Rows[0][ProjName]), Convert.ToInt32(dt.Rows[0][MinDay]), Convert.ToInt32(dt.Rows[0][MinWeek]), Convert.ToInt32(dt.Rows[0][DayWeek])); //Works Fine
Constructor for the Project Class:
public Project(string projectName, int hoursPerDay, int hoursPerWeek, int daysPerWeek)
You have stated in your answer is works when converting, and it is necessary as they are not strings and integers. They are objects. You can create a methid to handle it if you want.
public Project CreateProject(object projectName, object hoursPerDay, object hoursPerWeek, object daysPerWeek)
{
return new Project(projectName.ToString(), Convert.ToInt32(hoursPerDay), Convert.ToInt32(hoursPerWeek), Convert.ToInt32(daysPerWeek);
}
You have to explicitly cast the objects:
To cast to string use:
Object.ToString();
To cast to integers use:
Int32.TryParse(String, out int);
Your constuctor becomes
Project = new Project(dt.Rows[0][ProjName].ToString(), Int32.Parse(dt.Rows[0][MinDay]), Int32.Parse(dt.Rows[0][MinWeek]), Int32.Parse(dt.Rows[0][DayWeek]));
Note: Using Int32.Parse instead of Int32.TryParse assumes that the argument provided is a valid int at all times and does not give you a way to check if the casting has succeeded.
dt.Rows[0][ProjName] returns type object, and your method expects string. Even though you know it to be a string, it is not obvious to the compiler and must be specified explicitly using a cast, as you show in your last example, although just casting should be more efficient than converting unnecessarily:
Project = new Project((string)dt.Rows[0][ProjName], ...

Categories

Resources