在一个程序中,我使用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作为附加信息,看看它是否有帮助.)