【发布时间】:2014-03-26 20:51:13
【问题描述】:
在一个程序中,我使用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");
}
}
如果新的实例实现了ISortedSet<TKey> 接口,则该程序通常会打印“set”并打印“object”以获取其他任何内容。但是,使用以下声明会抛出 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,但我很想知道这里发生了什么。我的类声明在某种程度上不受支持吗?
不知道为什么会发生这种情况让我担心我们在其他地方使用dynamic 关键字。我可以不相信吗?
【问题讨论】:
-
C# 是否支持基于动态调用重载,我不知道,但您可以通过 RTTI 手动完成。在 (object obj) 重载中,检查类型、强制转换并将其传递给适当的重载。抱歉,如果这没有帮助。您可以将强制转换为动态和对 Print 的调用分离为单独的语句吗?这将有助于确定是导致问题的动态转换,还是试图找到合适的打印重载。
-
像这样的动态方法调用工作正常通常。只是它显然有时会导致堆栈溢出。
-
@Brent:是的,我会更新代码。同样的行为。
-
假设您使用的是 Visual Studio,请关闭“Step into just my code”并尝试进入通话。如果这不能让您观察到堆栈溢出的发生,请尝试切换到反汇编。即使您不了解汇编程序,您也应该能够在调用堆栈崩溃之前观察到大量递归链。如果它是在非你的代码中完成这一切,那么我建议创建一个错误报告。