Недействительные генерируемые исключения
У меня возникла эта проблема, я использую отражение, чтобы вытащить свойства из класса, но проблема в отражении возвращает их как объект, и я не могу получить его в моем фактическом типе.
Возьмем, к примеру, если это класс:
public class Row<T>
{
public static explicit operator Row<object>(Row<T> o)
{
return new Row<object>
{
Name = o.Name,
Value = o.Value
};
}
public string Name { get; set; }
public T Value { get; set; }
}
Кастинг от одного слова Row<bool>
до Row<object>
работает:
var a = new Row<bool>
{
Name = "Foo",
Value = true
};
var b = (Row<object>)a; // Works
Но когда я пытаюсь перейти от object
к Row<object>
, он, кажется, игнорирует мой явный оператор и бросает System.InvalidCastException:
var c = (object) a; // Simulate getting from reflection
var d = (Row<object>) c; // System.InvalidCastException
Что мне не хватает?
Ответы
Ответ 1
Проблема заключается в том, что кастинг не ищет оператора преобразования, если он не задан для статического типа значения, которое вы пытаетесь выполнить. В вашем примере статический тип c
равен object
, а object
не имеет и не имеет оператора преобразования в Row<object>
, что приводит к исключению среды выполнения.
Похоже, эта проблема может быть легко обойдена с лучшей конструкцией.
Вы хотите обрабатывать любой тип Row<T>
как Row<object>
, а оператор преобразования не более чем работает с тем, что эти типы не связаны иерархически. Так почему бы не сделать их связанными и избежать проблемы в первую очередь?
Например:
public abstract class Row
{
public string Name { get; set; }
public object Value { get; protected set; }
}
public class Row<T> : Row
{
public new T Value
{
get { return (T)base.Value; }
set { base.Value = value; }
}
}
Это похоже на то, что вы хотите:
- Проблема кастинга решена, потому что теперь вы можете применить любой тип
Row<T>
к базовому классу Row
(который берет на себя ответственность Row<object>
в вашем первоначальном дизайне) и легко доступен Name
и Value
независимо от типа Value
.
- Установитель
Row.Value
защищен, поэтому вы не можете отбрасывать Row<int>
до Row
и делать Value
, например. a string
снаружи, сохраняя безопасность типов.
Ответ 2
Используйте dynamic
вместо object
для принудительной проверки реального времени выполнения:
var c = (dynamic)a;
var d = (Row<object>)c; // Works fine
Он вызывет ваш оператор Row<T> -> Row<object>
.
Ответ 3
Вы можете выполнить это с отражением:
public class RowHelper
{
public static Row<object> LoadRow(object o)
{
var type = o.GetType();
return new Row<object>
{
Name = (string)type.InvokeMember("Name", BindingFlags.GetProperty, null, o, null),
Value = type.InvokeMember("Value", BindingFlags.GetProperty, null, o, null)
};
}
}
Вы бы назвали это с помощью:
var d = RowHelper.LoadRow(c);