当通过动态访问通用类型的成员时,StackOverflowException:.NET / C#框架错误? [英] StackOverflowException when accessing member of generic type via dynamic: .NET/C# framework bug?

查看:63
本文介绍了当通过动态访问通用类型的成员时,StackOverflowException:.NET / C#框架错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在程序中,我使用动态关键字来调用最佳匹配方法。然而,我发现在某些情况下,框架崩溃了一个 StackOverflowException



我试图简化我的代码尽可能地仍然能够重新产生这个问题。

  class Program 
{
static void Main(string [] args)
{
var obj = new SetTree< int>();
var dyn =(dynamic)obj;
Program.Print(dyn); //抛出StackOverflowException!
//注意:这对其他都很好,但是我的SetTree< T>
}
static void Print(object obj)
{
Console.WriteLine(object);
}

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

该程序通常 如果新增实例实现了 ISortedSet< TKey> 接口并打印对象,则打印set。但是,使用以下声明,将抛出一个 StackOverflowException (如上述注释所示)。

  interface ISortedSet< TKey> {} 

密封类SetTree< TKey> :BalancedTree< SetTreeNode< TKey>>,ISTEDSet< TKey> {}

抽象类BalancedTree< TNode>
其中TNode:TreeNode< TNode> {}

抽象类SetTreeNode< TKey> :KeyTreeNode< SetTreeNode< TKey>,TKey> {}

抽象类KeyTreeNode< TNode,TKey> :TreeNode< TNode>
其中TNode:KeyTreeNode< TNode,TKey> {}

抽象类TreeNode< TNode>
其中TNode:TreeNode< TNode> {}

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



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



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





在该位置注册转储:

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

这不是告诉我更多的是一个指标,这确实是一个框架中的某种错误。



我已提交了Microsoft Connect的错误报告但我有兴趣知道这里发生了什么。我的类声明在某种程度上不受支持吗?



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

解决方案

我创建了一个更短的更多的 SSCCE ,说明了这个问题:

  class Program 
{
static void Main()
{
dynamic obj = new Third< int>();
打印(obj); //导致堆栈溢出
}

static void Print(object obj){}
}

class First< T>其中T:First< T> {}

class Second< T> :第一< T>其中T:First< T> {}

class Third< T> :第二< Third>>查看调用堆栈,似乎在C#中的两对符号之间弹跳,运行时绑定器:

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


Microsoft.CSharp.RuntimeBinder.SymbolTable.GetConstructedType(
System.Type类型,
Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg

  Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeCore(
Microsoft.CSharp.RuntimeBinder.Semantics.CType类型,
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上提交一个bug;如果编译器没有被这个捕获,那么运行时绑定程序可能不应该是。






此代码示例运行正确地:

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

static void Print(object obj){}
}

内部类首先< T>
其中T:First< T> {}

内部类Second< T> :第一< Second< T> {}

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


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 StackOverflowException under some circumstances.

I have tried to simplify my code as much as possible while still being able to re-produce this problem.

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");
    }
}

That program would normally print "set" if the newed up instance implements the ISortedSet<TKey> interface and print "object" for anything else. But, with the following declarations a StackOverflowException is thrown instead (as noted in a comment above).

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> { }

Whether this is a bug or not it is very troubling that a StackOverflowException is thrown as we are unable to catch it and also pretty much unable to determine in advance whether an exception will be thrown (and thereby terminate the process!).

Can someone please explain what's going on? Is this a bug in the framework?

When debugging and switching to "Disassembly mode" I'm seeing this:

Register dump at that location:

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

That doesn't tell me much more than being an indicator that this indeed must be some kind of bug in the framework.

I've filed a bug report on Microsoft Connect but I'm interested in knowing what's going on here. Are my class declarations unsupported in some way?

Not knowing WHY this is happening causes me to worry about other places where we are using the dynamic keyword. Can I not trust that at all?

解决方案

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

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>> { }

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<int>();
        Print(obj);
    }

    static void Print(object obj) { }
}

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

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

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.)

这篇关于当通过动态访问通用类型的成员时,StackOverflowException:.NET / C#框架错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆