Я в настоящее время работаю над приложением, где я должен загрузить данные из базы данных SQL и затем присвоить полученные значения в свойства объекта. Я делаю это при помощи отражения начиная с имен свойства, и имена столбцов являются тем же. Однако многие свойства используют пользовательский тип структуры, который является в основном оберткой валюты для десятичного типа. Я определил неявное преобразование в своей структуре:
public static implicit operator Currency(decimal d)
{
return new Currency(d);
}
Это хорошо работает, когда я использую его в коде. Однако, когда у меня есть это:
foreach (PropertyInfo p in props)
{
p.SetValue(this, table.Rows[0][p.Name], null);
}
Это бросает ArgumentException, указывая, что это не может преобразовать из Системы. Десятичное число к Валюте. Я смущен, так как это хорошо работает при любом другом обстоятельстве.
Я думаю, что вам нужно сначала раскрыть значение в table.Rows[0][p.Name]
как десятичную дробь
.
Другими словами:
foreach (PropertyInfo p in props)
{
if (p.PropertyType == typeof(Currency))
{
Currency c = (decimal)table.Rows[0][p.Name];
p.SetValue(this, c, null);
}
else
{
p.SetValue(this, table.Rows[0][p.Name], null);
}
}
Эту проблему я встречал один или два раза раньше, поэтому я решил написать об этом статью в блоге. Если кому-то нужно немного больше объяснений, не стесняйтесь, прочитайте.
К сожалению, эти определяемые пользователем операторы преобразования не используются во время выполнения; они используются только компилятором во время компиляции. Поэтому если вы возьмете сильно типизированный decimal
и присвоите его сильно типизированному Currency
, компилятор вставит вызов вашего оператора преобразования, и все будут довольны. Однако, когда вы вызываете SetValue
, как вы это делаете здесь, среда выполнения ожидает, что вы передадите ей значение соответствующего типа; среда выполнения понятия не имеет о существовании этого оператора преобразования и никогда его не вызовет.
Я предполагаю, что table
в вашем коде имеет тип DataTable
, поэтому первый индексатор возвращает DataRow
, а второй - object
. Тогда PropertyInfo.SetValue
также принимает object
в качестве второго аргумента. Ни в одном месте этого кода не происходит приведения, поэтому перегруженный оператор преобразования не применяется.
Вообще говоря, он применяется только тогда, когда известны статические типы (забывая пока о динамических
в C# 4.0). Он не применяется при боксировании и дебоксировании вещей. В данном случае индексатор на DataRow
фиксирует значение, а PropertyInfo.SetValue
пытается разгруппировать его к другому типу - и терпит неудачу.
Хотя я не отвечаю на вашу проблему, я думаю, что в такой ситуации было бы более уместно использовать ORM Framework, например Entity Framework или NHibernate, который отобразит ваши таблицы в ваши доменные объекты и обработает все преобразования за вас. Использование чего-то вроде рефлексии для выяснения того, какие поля нужно заполнить в доменном объекте, - медленный способ сделать это.