2

I met a weird error in my job, it happened when I tried to access a dynamic property in a special method of a special class. After researching, I found the code that made that problem but I still don't know why.

Here is my code ( I use .NET 4.5 )

public class MyCommand<TResult>
    : Command<MyCommand<TResult>>
   where TResult : class
{
}

public class MyDacAction : DacActionBase<MyDacAction, MyCommand<string>>
{
    public override void Execute()
    {
        dynamic x = new System.Dynamic.ExpandoObject();
        x.AAA = 100;
        int b = x.AAA;
    }
}

public abstract class DacActionBase<TCommand, TDacCommand>
    where TCommand : class
    where TDacCommand : class, new()
{
    public virtual void Execute()
    {

    }
}

public abstract class Command<TCommand>
   : CommandBase<TCommand>
    where TCommand : class
{
    public virtual void Execute()
    {
    }
}

public abstract class CommandBase<TCommand> where TCommand : class
{
}

class Program
{
    static void Main(string[] args)
    {
        var test = new MyDacAction();
        test.Execute();
    }
}

If you create a console app and run this code, you will see the StackOverflowException at this line

int b = x.AAA;

When testing, I found two changes that the error will not be threw

1.

public class MyCommand
    : Command<MyCommand>
{
}

public class MyDacAction : DacActionBase<MyDacAction, MyCommand>
{
    public override void Execute()
    {
        dynamic x = new System.Dynamic.ExpandoObject();
        x.AAA = 100;
        int b = x.AAA;
    }
}

2.

public abstract class Command<TCommand>
    where TCommand : class
{
    public virtual void Execute()
    {
    }
}

Can you tell me why this error happened ?

  • A stack overflow exception mainly occurs because something has filled the stack to the brim, the actual code location that overflows the stack may or may not be relevant to the problem. Can you ensure that the stack is not already full when you get to that method? – Lasse V. Karlsen Oct 05 '16 at 09:42
  • Looks like a Microsoft bug! If you comment out the line `int b = x.AAA;` it works fine! I would report this on Microsoft.Connect. – Matthew Watson Oct 05 '16 at 09:51
  • 2
    Who voted to close this as unclear? The OP has provided compilable code that demonstrates the problem! – Matthew Watson Oct 05 '16 at 09:54
  • 2
    Looks similar to this [one](http://stackoverflow.com/questions/19612325/stackoverflowexception-when-accessing-member-of-nested-class-via-a-dynamic-refer) and this [other](http://stackoverflow.com/questions/22672775/stackoverflowexception-when-accessing-member-of-generic-type-via-dynamic-net-c) one ... and I'd say that the ExpandoObject is not needed, just dynamic x = 0 should be enough... –  Oct 05 '16 at 10:33
  • @MachineLearning You are correct: I just tried it with `dynamic x = 0; int y = x;` and it fails (note, however, that you need the `y = x;`). It looks like that's the answer. It's a Microsoft bug. – Matthew Watson Oct 05 '16 at 11:12
  • @MatthewWatson Sure. I tried to edit my comment again (to add it clearly) after 5 minutes (and I didn't want to comment again :-) ) But - instead of `int y = x;` you you can do `Print(x);` with `static void Print(object obj) { }` to be on the **same** page of the **nice** SSCCE posted in the first answer of my second link above –  Oct 05 '16 at 11:23
  • Note that the bug was reported to Microsoft in 2014, but it hasn't been fixed yet (and the link to the bug is dead!) – Matthew Watson Oct 05 '16 at 11:34
  • @MatthewWatson It is this open [issue](https://github.com/dotnet/corefx/issues/7527) –  Oct 05 '16 at 11:39

1 Answers1

3

There is an open issue #7527 in the CoreFX repository

Dynamic type resolution goes into infinite recursion

with the following repro.txt for this bug.

namespace SOE2
{
    class Program
    {
        static void Main(string[] args)
        {
            // vvv--- changing "dynamic" to "var" or "string" here fixes the issue
            dynamic parsedDocument = "";

            var mystery = new FailingClass<string>();

            Console.WriteLine("Entering...");
            mystery.FailingMethod(parsedDocument);
            Console.WriteLine("... and we are back!");
            Console.ReadLine();
        }
    }

    public abstract class CommonBase<T>
    {
    }

    // Technically, this class does nothing and deriving from it should be identical to deriving from CommonBase
    public abstract class FailingClassBase<T> : CommonBase<T>
    {
    }

    // However, deriving from CommonBase instead of FailingClassBase here also fixes the issue
    // ----------------------------vvvvvvvvvvvvvvvv
    public class FailingClass<T> : FailingClassBase<FailingClass<T>>
    {
        public void FailingMethod(T src)
        {
        }
    }
}