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