Подтвердить что ты не робот

Недействительные генерируемые исключения

У меня возникла эта проблема, я использую отражение, чтобы вытащить свойства из класса, но проблема в отражении возвращает их как объект, и я не могу получить его в моем фактическом типе.

Возьмем, к примеру, если это класс:

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

Что мне не хватает?

4b9b3361

Ответ 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);