StackOverflowException when accessing member of generic type via dynamic: .NET/C# framework bug?

前端 未结 3 1714
猫巷女王i
猫巷女王i 2020-12-06 11:24

In a program I\'m using the dynamic keyword to invoke the best matching method. However, I have found that the framework crashes with a StackOverflowExcep

3条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-06 11:40

    I created a shorter, more to-the-point SSCCE that illustrates the problem:

    class Program
    {
        static void Main()
        {
            dynamic obj = new Third();
            Print(obj); // causes stack overflow
        }
    
        static void Print(object obj) { }
    }
    
    class First where T : First { }
    
    class Second : First where T : First { }
    
    class Third : Second> { }
    

    Looking at the call stack, it seems to be bouncing between two pairs of symbols in the C# runtime binder:

    Microsoft.CSharp.RuntimeBinder.SymbolTable.LoadSymbolsFromType(
        System.Type originalType
    )
    
    Microsoft.CSharp.RuntimeBinder.SymbolTable.GetConstructedType(
        System.Type type,
        Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg
    )
    

    and

    Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeCore(
        Microsoft.CSharp.RuntimeBinder.Semantics.CType type, 
        Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx
    )
    
    Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeArray(
        Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray taSrc,
        Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx
    )
    

    If I had to hazard a guess, some of the generic type constraint nesting you've got going on has managed to confuse the binder into recursively walking the types involved in the constraints along with the constraints themselves.

    Go ahead and file a bug on Connect; if the compiler doesn't get caught by this, the runtime binder probably shouldn't either.


    This code example runs correctly:

    class Program
    {
        static void Main()
        {
            dynamic obj = new Second();
            Print(obj);
        }
    
        static void Print(object obj) { }
    }
    
    internal class First
        where T : First { }
    
    internal class Second : First> { }
    

    This leads me to believe (without much knowledge of the internals of the runtime binder) that it's proactively checking for recursive constraints, but only one level deep. With an intermediary class in between, the binder ends up not detecting the recursion and tries to walk it instead. (But that's all just an educated guess. I'd add it to your Connect bug as additional information and see if it helps.)

提交回复
热议问题