Я создал функцию, которая принимает команду SQL и выдает результат, который затем можно использовать для заполнения списка экземпляров класса. Код работает отлично. Я включил здесь немного упрощенную версию без обработки исключений только для справки — пропустите этот код, если хотите сразу решить проблему. Если у вас есть предложения здесь, я все уши.
public List<T> ReturnList<T>() where T : new()
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
Type objectType = typeof (T);
FieldInfo[] typeFields = objectType.GetFields();
while (nwReader.Read())
{
T obj = new T();
foreach (FieldInfo info in typeFields)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
Как я уже сказал, это работает просто отлично. Однако я хотел бы иметь возможность вызывать аналогичную функцию с анонимным классом по очевидным причинам.
Вопрос № 1: кажется, что я должен создать экземпляр анонимного класса при вызове моей анонимной версии этой функции - правильно ли это? Пример вызова:
.ReturnList(new { ClientID = 1, FirstName = "", LastName = "", Birthdate = DateTime.Today });
Вопрос №2: анонимная версия моей функции ReturnList ниже. Может ли кто-нибудь сказать мне, почему вызов info.SetValue просто ничего не делает? Он не возвращает ошибку или что-то еще, но и не меняет значение целевого поля.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
// Cannot use FieldInfo[] on the type - it finds no fields.
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
Любые идеи?
Примечание: когда я пытался использовать массив FieldInfo, как я делал в функции выше, массив typeFields не содержал элементов (даже несмотря на то, что objectType показывает имена полей — странно). Поэтому вместо этого я использую TypeDescriptor.GetProperties.
Любые другие советы и рекомендации по использованию отражения или анонимных классов здесь уместны - я относительно новичок в этом конкретном уголке языка С#.
ОБНОВЛЕНИЕ: я должен поблагодарить Джейсона за ключ к решению этой проблемы. Ниже приведен измененный код, который создаст список экземпляров анонимного класса, заполняя поля каждого экземпляра из запроса.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
int objIdx = 0;
object[] objArray = new object[properties.Count];
foreach (PropertyDescriptor info in properties)
objArray[objIdx++] = nwReader[info.Name];
fdList.Add((T)Activator.CreateInstance(sample.GetType(), objArray));
}
nwReader.Close();
return fdList;
}
Обратите внимание, что запрос был создан, а параметры были инициализированы в предыдущих вызовах методов этого объекта. Исходный код имел комбинацию внутреннего/внешнего цикла, так что пользователь мог иметь поля в своем анонимном классе, которые не соответствовали полю. Однако, чтобы упростить дизайн, я решил не допускать этого и вместо этого принял доступ к полю db, рекомендованный Джейсоном. Также спасибо Дейву Марклу за то, что он помог мне лучше понять компромиссы между использованием Activator.CreateObject() и GenUninitializedObject.