Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullReferenceException thrown by Microsoft.CSharp when using Generic Interfaces and Dynamic #114

Closed
AmitGee opened this issue Jun 8, 2014 · 3 comments

Comments

@AmitGee
Copy link

AmitGee commented Jun 8, 2014

I posted this to code.google.com (#376) before realising that the site had moved. The following code produces a NullRef exception

I'm using Win7, Moq 4.2.1402.2112, .NET 4.5.1

The exception occurs when using a mock of the generic interface, when the type argument is of type object. It doesn't occur when:

  1. using a custom reference type or a value type as the type argument,
  2. using a mock of the non-generic interface,
  3. not using moq

The full stack trace of the error is below the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Moq;

namespace TestDynamic
{
    public interface IInterface<T>
    {
        T DoStuff(T input);
    }

    public interface IInterface
    {
        object DoStuff(object input);
    }

    class Program
    {
        static void Main(string[] args)
        {
            var m = new MockRepository(MockBehavior.Default);
            var mi = m.Create<IInterface>();
            var miGeneric = m.Create<IInterface<object>>();
            var miGenericString = m.Create<IInterface<string>>();

            var input = new object();
            var expectedOutput = new object();

            mi.Setup(i => i.DoStuff(input)).Returns(expectedOutput);
            miGeneric.Setup(i => i.DoStuff(input)).Returns(expectedOutput);
            miGenericString.Setup(i => i.DoStuff("input")).Returns("output");

            var p = new Program();

            //var output = p.NonGenericDoStuff(mi.Object, input); // ok
            var output = p.GenericDoStuff(miGeneric.Object, input);// throws
            //var output = p.GenericDoStuff(miGenericString.Object, "input"); // ok

            Console.WriteLine(output);
        }

        private T GenericDoStuff<T>(IInterface<T> obj, T input)
        {
            dynamic o = obj;
            var output = o.DoStuff(input);
            return output; // throws NullRef
        }


        private object NonGenericDoStuff(IInterface obj, object input)
        {
            dynamic o = obj;
            var output = o.DoStuff(input);
            return output;
        }
    }
}

Stack trace:

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.GenerateLambda(EXPRCALL pExpr)
   at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.VisitCALL(EXPRCALL pExpr)
   at Microsoft.CSharp.RuntimeBinder.Semantics.ExprVisitorBase.Dispatch(EXPR pExpr)
   at Microsoft.CSharp.RuntimeBinder.Semantics.ExprVisitorBase.Visit(EXPR pExpr)
   at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.Rewrite(TypeManager typeManager, EXPR pExpr, IEnumerable`1 listOfParameters)

   at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.CreateExpressionTreeFromResult(IEnumerable`1 parameters, ArgumentObject[] arguments, Scop
e pScope, EXPR pResult)
   at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.BindCore(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] a
rgs, DynamicMetaObject& deferredBinding)
   at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.Bind(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args,
 DynamicMetaObject& deferredBinding)
   at Microsoft.CSharp.RuntimeBinder.BinderHelper.Bind(DynamicMetaObjectBinder action, RuntimeBinder binder, IEnumerable`1 args, IEnumerable
`1 arginfos, DynamicMetaObject onBindingError)
   at Microsoft.CSharp.RuntimeBinder.CSharpConvertBinder.FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
   at System.Dynamic.DynamicMetaObject.BindConvert(ConvertBinder binder)
   at System.Dynamic.ConvertBinder.Bind(DynamicMetaObject target, DynamicMetaObject[] args)
   at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
   at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at TestDynamic.Program.GenericDoStuffDynamic[T](IInterface`1 obj, T input) in c:\TFS\TestProjects\TestDynamic\TestDynamic\Program.cs:line
 37
   at TestDynamic.Program.Main(String[] args) in c:\TFS\TestProjects\TestDynamic\TestDynamic\Program.cs:line 27
@kzu
Copy link
Contributor

kzu commented Jun 9, 2014

» dynamic o = obj; var output = o.DoStuff(input);

Why make obj dynamic? If you just invoke obj.DoStuff(input) it should just
work

/kzu from mobile

@AmitGee
Copy link
Author

AmitGee commented Jun 9, 2014

The code above is just to reproduce the issue. In the real application I'm using dynamic for implementing a multiple-dispatch pattern (visitor..), and stumbled on this error when unit testing it. I have multiple DoStuff() methods, each taking in a specialization of IInterface as its parameter. Using dynamic allows me to invoke the correct DoStuff method based on the runtime type of the IInterface object. I can post a longer sample that's closer to my application if that helps.
Thanks!!

@stakx
Copy link
Contributor

stakx commented Jun 21, 2017

@AmitGee: Moq actually handles this just fine. You might have noticed that the exception does not get thrown by the mock invocation, but by the return statement in your method:

private T GenericDoStuff<T>(IInterface<T> obj, T input)
{
    dynamic o = obj;
    var output = o.DoStuff(input);
    return output; // throws NullRef
}

That is, you have received a return value from the mock, and it is stored in output. From this point onward, Moq is out of the picture.

What happens next is that the Dynamic Language Runtime cannot convert the value in output directly to type T. See what happens when you replace return output; with return (T)(object)output;, i.e. when you first "cast" from dynamic to object, and then have a regular type cast from object to T. ;-)

@stakx stakx closed this as completed Jun 21, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants