当通过动态访问通用类型的成员时,StackOverflowException:.NET / C#框架错误?

一个程序中,我使用dynamic关键字来调用最佳匹配方法.但是,在某些情况下,我发现框架崩溃了StackOverflowException.

我尽可能地简化了我的代码,同时仍然能够重新生成这个问题.

class Program
{
    static void Main(string[] args)
    {
        var obj = new SetTree<int>();
        var dyn = (dynamic)obj;
        Program.Print(dyn); // throws StackOverflowException!!
        // Note: this works just fine for 'everything else' but my SetTree<T>
    }
    static void Print(object obj)
    {
        Console.WriteLine("object");
    }

    static void Print<TKey>(ISortedSet<TKey> obj)
    {
        Console.WriteLine("set");
    }
}

如果新增的实例实现了ISTEDSet< TKey>,则该程序通常将打印“set”界面和打印“对象”的其他任何东西.但是,使用以下声明,将抛出一个StackOverflowException(如上述注释中所述).

interface ISortedSet<TKey> { }

sealed class SetTree<TKey> : BalancedTree<SetTreeNode<TKey>>,ISortedSet<TKey> {}

abstract class BalancedTree<TNode> 
    where TNode : TreeNode<TNode> { }

abstract class SetTreeNode<TKey> : KeyTreeNode<SetTreeNode<TKey>,TKey> { }

abstract class KeyTreeNode<TNode,TKey> : TreeNode<TNode>
    where TNode : KeyTreeNode<TNode,TKey> { }

abstract class TreeNode<TNode>
    where TNode : TreeNode<TNode> { }

这是否是一个错误,因为我们无法捕捉到StackOverflowException是非常麻烦的,并且几乎无法提前确定是否抛出异常(从而终止进程!).

有人可以解释发生了什么吗?这是框架中的错误吗?

当调试并切换到“拆卸模式”时,我看到:

在该位置注册转储:

EAX=02B811B4 EBX=0641EA5C ECX=02C3B0EC EDX=02C3A504 ESI=02C2564C
EDI=0641E9AC EIP=011027B9 ESP=0641E91C EBP=0641E9B8 EFL=00000202

这不仅仅是一个指标,它确实必须是框架中的某种错误.

我有filed a bug report on Microsoft Connect,但我有兴趣知道这里发生了什么.我的类声明在某种程度上不受支持吗?

不知道为什么发生这种情况导致我担心使用动态关键字的其他地方.我可以不信任吗?

解决方法

我创建了一个较短的,更加明确的 SSCCE,说明了这个问题:
class Program
{
    static void Main()
    {
        dynamic obj = new Third<int>();
        Print(obj); // causes stack overflow
    }

    static void Print(object obj) { }
}

class First<T> where T : First<T> { }

class Second<T> : First<T> where T : First<T> { }

class Third<T> : Second<Third<T>> { }

看看调用堆栈,似乎在C#运行时绑定器中的两对符号之间弹起来:

Microsoft.CSharp.RuntimeBinder.SymbolTable.LoadSymbolsFromType(
    System.Type originalType
)

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

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
)

如果我不得不冒犯一些猜测,一些通常的类型约束嵌套你已经进行了一切设法混淆了绑定器,循序渐进地介绍了约束条件以及约束本身.

继续在Connect上提交错误;如果编译器没有被这个捕获,那么运行时绑定可能也不应该.

代码示例正确运行:

class Program
{
    static void Main()
    {
        dynamic obj = new Second<int>();
        Print(obj);
    }

    static void Print(object obj) { }
}

internal class First<T>
    where T : First<T> { }

internal class Second<T> : First<Second<T>> { }

这导致我相信(没有关于运行时绑定的内部知识),它主动检查递归约束,但只有一个级别.在中间类之间,绑定器最终没有检测到递归,并试图走走它. (但这只是一个有教养的猜测,我会将其添加到您的Connect bug作为附加信息,看看它是否有帮助.)

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...