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

前端之家收集整理的这篇文章主要介绍了当通过动态访问通用类型的成员时,StackOverflowException:.NET / C#框架错误?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在一个程序中,我使用dynamic关键字来调用最佳匹配方法.但是,在某些情况下,我发现框架崩溃了StackOverflowException.

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

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var obj = new SetTree<int>();
  6. var dyn = (dynamic)obj;
  7. Program.Print(dyn); // throws StackOverflowException!!
  8. // Note: this works just fine for 'everything else' but my SetTree<T>
  9. }
  10. static void Print(object obj)
  11. {
  12. Console.WriteLine("object");
  13. }
  14.  
  15. static void Print<TKey>(ISortedSet<TKey> obj)
  16. {
  17. Console.WriteLine("set");
  18. }
  19. }

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

  1. interface ISortedSet<TKey> { }
  2.  
  3. sealed class SetTree<TKey> : BalancedTree<SetTreeNode<TKey>>,ISortedSet<TKey> {}
  4.  
  5. abstract class BalancedTree<TNode>
  6. where TNode : TreeNode<TNode> { }
  7.  
  8. abstract class SetTreeNode<TKey> : KeyTreeNode<SetTreeNode<TKey>,TKey> { }
  9.  
  10. abstract class KeyTreeNode<TNode,TKey> : TreeNode<TNode>
  11. where TNode : KeyTreeNode<TNode,TKey> { }
  12.  
  13. abstract class TreeNode<TNode>
  14. where TNode : TreeNode<TNode> { }

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

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

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

在该位置注册转储:

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

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

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

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

解决方法

我创建了一个较短的,更加明确的 SSCCE,说明了这个问题:
  1. class Program
  2. {
  3. static void Main()
  4. {
  5. dynamic obj = new Third<int>();
  6. Print(obj); // causes stack overflow
  7. }
  8.  
  9. static void Print(object obj) { }
  10. }
  11.  
  12. class First<T> where T : First<T> { }
  13.  
  14. class Second<T> : First<T> where T : First<T> { }
  15.  
  16. class Third<T> : Second<Third<T>> { }

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

  1. Microsoft.CSharp.RuntimeBinder.SymbolTable.LoadSymbolsFromType(
  2. System.Type originalType
  3. )
  4.  
  5. Microsoft.CSharp.RuntimeBinder.SymbolTable.GetConstructedType(
  6. System.Type type,Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg
  7. )

  1. Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeCore(
  2. Microsoft.CSharp.RuntimeBinder.Semantics.CType type,Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx
  3. )
  4.  
  5. Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeArray(
  6. Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray taSrc,Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx
  7. )

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

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

代码示例正确运行:

  1. class Program
  2. {
  3. static void Main()
  4. {
  5. dynamic obj = new Second<int>();
  6. Print(obj);
  7. }
  8.  
  9. static void Print(object obj) { }
  10. }
  11.  
  12. internal class First<T>
  13. where T : First<T> { }
  14.  
  15. internal class Second<T> : First<Second<T>> { }

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

猜你在找的C#相关文章