afeygin
afeygin

Reputation: 1283

Why does this runtime dynamic binding fail?

Why does the following test fail?

[TestClass]
public class DynamicTests
{
    public class ListOfIntsTotaller
    {
        public float Total(List<int> list) { return list.Sum(); }
    }
    public static class TotalFormatter
    {
        public static string GetTotal(IEnumerable list, dynamic listTotaller)
        {
            //  Get a string representation of a sum
            return listTotaller.Total(list).ToString();
        }
    }
    [TestMethod]
    public void TestDynamic()
    {
        var list = new List<int> { 1, 3 };
        var totaller = new ListOfIntsTotaller();
        Assert.AreEqual("4", totaller.Total(list).ToString()); // passes 
        Assert.AreEqual("4", TotalFormatter.GetTotal(list, totaller)); // fails
    }
}

With the following error:

Test method MyTests.DynamicTests.TestDynamic threw exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'MyTests.DynamicTests.ListOfIntsTotaller.Total(System.Collections.Generic.List<int>)'
has some invalid arguments

Shouldn't the binder be smart enough to match list to its underlying type of List<int> and thus successfully bind to the GetTotal method?

Upvotes: 5

Views: 7534

Answers (4)

Knickedi
Knickedi

Reputation: 8787

That's an addition to Guffa's answer (which is still completely valid).

Like afeygin I thought that a dynamic call would check the right underlying type.
But in my situation I had no chance to modify the interface because of the lack of information.

Here's the example:

// I know that doit will be a certain type like MyDoIt but i can't know the actual type
// The only information I have is that MyDoIt is implementing IToBeDone 
public void DoIt(IToBeDone doit)
{
    // I know that _doItInstance.DoIt will expect MyDoIt but can't know the actual
    // type here so this will throw
    _doItInstance.DoIt(doit);
}

Here's a workaround:

public void DoIt(IToBeDone doit)
{
    // this will cause that IToBeDone won't be used for type resolution but the real
    // type instead (in my case MyDoIt)
    dynamic dynDoit = doit;
    _doItInstance.DoIt(dynDoit);
}

Upvotes: 1

Birey
Birey

Reputation: 1802

It is because while calling the function Total, IEnumerable cannot be converted to a type of List.

List implements  IList<T>, ICollection<T>, 
          IEnumerable<T>, IList, ICollection, IEnumerable

passing

IEnumerable<int>

to both Total and GetTotal would do the trick.

Upvotes: 4

Guffa
Guffa

Reputation: 700152

The problem is that list in the GetTotal method is not a List<int>.

The dynamic call is determined based on the type of the variable that you use, not the actual type of the object that it's pointing to. The method Total takes a List<int>, not an IEnumerable.

Upvotes: 5

Jean-Bernard Pellerin
Jean-Bernard Pellerin

Reputation: 12670

it's the fact that you can't cast from IEnumerable to List<int>
Try it with this line instead

public float Total(IEnumerable<int> list) { return list.Sum(); }

so it's not your dynamic failing, it's the function call not passing in valid arguments

Upvotes: 2

Related Questions