StackOverflowException при доступе к члену вложенного класса с помощью динамической ссылки

Я определил общий класс, который происходит из BindingList и имеет вложенный не общий класс:

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

A StackOverflowException происходит в mscorlib при попытке получить доступ к свойству Value с помощью динамической ссылки, например:

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, программа выполняется правильно.

Почему это происходит?

Edit:

Это верхняя часть стека вызовов:

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

Ответы

Ответ 1

Большое спасибо за исправление! Я исследовал это, я бы сказал, очень интересный момент и обнаружил, что я прав.

Прежде всего, это не ОШИБКА! Именно так команда 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

Просто интерфейсы....

Поэтому он работает со списком, но не с BindingList! Мой пример доказывает это! Я считаю, что они сделали это намеренно, чтобы остановить бесконечный цикл.

Ответ 2

Я думаю, что проблема в этом месте

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>, чтобы разбить этот цикл инфинитива, и он работает!