Примечание. Следующий код на самом деле работает нормально, но показывает сценарий, который дает сбой в моем собственном решении. Дополнительную информацию смотрите в нижней части этого сообщения.
С этими классами:
public class MainType {
public static readonly MainType One = new MainType();
public static readonly MainType Two = SubType.Two;
}
public sealed class SubType : MainType {
public new static readonly SubType Two = new SubType();
}
Получить поля One
и Two
:
List<FieldInfo> fieldInfos = typeof(MainType)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(f => typeof(MainType).IsAssignableFrom(f.FieldType))
.ToList();
Наконец, получите их значения:
List<MainType> publicMainTypes = fieldInfos
.Select(f => (MainType) f.GetValue(null))
.ToList();
В LinqPad или в простом классе юнит-тестов с приведенным выше кодом все работает нормально. Но в моем решении, где у меня есть несколько модульных тестов, которые хотят работать со всеми экземплярами этих полей, GetValue
отлично работает для возврата полей родительского типа, но там, где предполагается, что родительские поля имеют экземпляры подтипа, они всегда вместо этого дают null
! (Если бы это произошло здесь, окончательный список был бы { One, null }
вместо { One, Two }
.) Тестовый класс находится в проекте, отличном от двух типов (каждый в своем собственном файле), но я временно сделал все публичный. Я установил точку останова и изучил все, что мог, и сделал эквивалент fieldInfos[1].GetValue(null)
в выражении Watch, и на самом деле он возвращает null, несмотря на то, что в моем основном классе есть строка точно такая же, как вторая один из MainType
выше.
Что не так? Как получить все значения полей подтипа? Как они вообще могут возвращать null без ошибки?
По теории, что, возможно, по какой-то причине класс подтипа не создавался статически из-за доступа через отражение, я попытался
System.Runtime.CompilerServices.RuntimeHelpers
.RunClassConstructor(typeof(SubType).TypeHandle);
вверху перед запуском, но это не помогло (где SubType
— фактический класс подтипа в моем проекте).
Я буду продолжать пытаться воспроизвести это в простом случае, но на данный момент у меня нет идей.
Дополнительная информация
После кучи возни код заработал. Сейчас опять не работает. Я работаю над воспроизведением того, что заставило код начать работать.
Примечание. Нацеливание на .Net 4.6.1 с использованием C# 6.0 в Visual Studio 2015.
Доступно воспроизведение проблемы
Вы можете поиграть с рабочей (неудачной) урезанной версией моего сценария, загрузив этот минимальный рабочий пример проблемы на github.
Отладьте модульные тесты. Когда возникает исключение, шагайте до тех пор, пока не дойдете до строки 20 GlossaryHelper.cs и не увидите возвращаемое значение GetGlossaryMembers
на вкладке Locals
. Вы можете видеть, что индексы с 3 по 12 равны нулю.
MainType
инициализировать переменную, используя член производного класса? Может ли базовый класс узнать свой производный класс, если производный класс определен после базового класса? - person Marson Mao   schedule 02.11.2016