通过动态引用访问嵌套类成员时的 StackOverflowException

本文关键字:成员 StackOverflowException 嵌套 动态 引用 访问 | 更新日期: 2023-09-27 18:37:08

我定义了一个派生自 BindingList 的泛型类,并具有一个嵌套的非泛型类:

class Generic<T> : BindingList<Generic<T>.Inner>
{
    public class Inner
    {
        public object Foo { get; set; }
    }
}

当尝试通过动态引用访问 Value 属性时,mscorlib 中会发生StackOverflowException,如下所示:

dynamic d = new Generic<string>.Inner();
var value = d.Foo; // StackOverflowException
var value = d.Bar    // StackOverflowException as well, not a 
                     // 'RuntimeBinderException' like you would expect when
                     // trying to access a non-existing member

这是我能够做的最小的复制品。

从 BindingList 派生是一个重要的细节,如果我将其更改为List程序可以正确执行。

为什么会这样?

编辑:

这是调用堆栈的顶部:

[Managed to Native Transition]  
mscorlib.dll!System.RuntimeTypeHandle.Instantiate(System.Type[] inst)   
mscorlib.dll!System.RuntimeType.MakeGenericType(System.Type[] instantiation)    
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.CType.CalculateAssociatedSystemTypeForAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateType aggtype)   
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.CType.CalculateAssociatedSystemType(Microsoft.CSharp.RuntimeBinder.Semantics.CType src)   
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.CType.AssociatedSystemType.get()  
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.GetAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg, Microsoft.CSharp.RuntimeBinder.Semantics.AggregateType atsOuter, Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray typeArgs)  
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.GetAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg, Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray typeArgsAll)    
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.GetAggregate(Microsoft.CSharp.RuntimeBinder.Semantics.AggregateSymbol agg, Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray typeArgsAll)    
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeCore(Microsoft.CSharp.RuntimeBinder.Semantics.CType type, Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx)    
Microsoft.CSharp.dll!Microsoft.CSharp.RuntimeBinder.Semantics.TypeManager.SubstTypeArray(Microsoft.CSharp.RuntimeBinder.Semantics.TypeArray taSrc, Microsoft.CSharp.RuntimeBinder.Semantics.SubstContext pctx)  

通过动态引用访问嵌套类成员时的 StackOverflowException

我认为问题出在这个地方

Generic<T> :BindingList<Generic<T>.Inner>

请注意,您将声明的类用作父类 BindingList 中的泛型参数。所以我相信反射最终会得到一个不定式循环,你会得到StackOverflow。

当您使用

var d = new Generic<string>.Inner();

编译器只是用Generic.Inner 替换它所以它是一样的

Generic<string>.Inner d = new Generic<string>.Inner();

但是当你使用

dynamic d = new Generic<string>.Inner();

你真的使用反射。反思再次开始深入挖掘你的阶级结构,就像......你的类 => BindingList => BindingList 的泛型参数 =>你的类(因为它是 BindingList 的泛型参数)=> BindingList =>依此类推,直到你得到 StackOverflow。

你可以改成Generic<T> : BindingList<string>来打破这个不定式循环,它有效!

非常感谢您的指正!我调查了这个我会说非常有趣的时刻,发现我是对的。

首先,这不是错误!这就是Microsoft团队解决这个问题的方式。再一次,我相信我上面写的所有内容都是真的!

所以正如我所说,你最终会得到一个不定式循环并得到 StackOverflow,但在我看来,你得到它非常非常快。因此,当您无法访问您的机器并且看起来它已经死了时,没有任何长时间。我开始深入研究 BindingList 的结构,以及这里的结果。

我创造了

class Example<T> : Level1<Example<T>>
{
    public string Name = "111";
}
public class Level1<T>
{
}

在主要

dynamic d = new Example<string>();
var value = d.Name;

它有效!然后我添加了另一个级别

public class Level1<T> : Level2<T>
{
}
public class Level2<T>
{
}

我得到了StackOverflow。我改成了

public class Level1<T> : Level2
{
}
public class Level2
{
}

它又起作用了!

所以我认为Microsoft的家伙刚刚说...所以这是没有办法通过并抛出异常后的最高级别。

现在让我们看看BindingList<T>

public class BindingList<T> : Collection<T>, 
    IBindingList, IList, ICollection, IEnumerable, ICancelAddNew, 
    IRaiseItemChangedEvents

通知Collection<T>

看看List<T>

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable

只是接口....

因此,它适用于列表,但不适用于绑定列表!我的例子证明了这一点!我相信他们故意这样做是为了阻止不定式循环。