2013-08-29 12 views
13

Mam ten problem, używam refleksji do przeciągania właściwości z klasy, ale problem jest odbiciem, który zwraca je jako obiekt i nie mogę go wprowadzić do mojego rzeczywistego typu.Nieprawidłowe generatory wyjątków rzutowania

Weźmy na przykład, jeśli jest to klasa:

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; } 
} 

Odlewy rzec Row<bool> do Row<object> prace:

var a = new Row<bool> 
    { 
     Name = "Foo", 
     Value = true 
    }; 

    var b = (Row<object>)a; // Works 

Ale gdy próbuję iść z object do Row<object> wydaje zignoruj ​​mojego operatora jawnego i wyślij wyjątek System.InvalidCastException:

var c = (object) a; // Simulate getting from reflection 

var d = (Row<object>) c; // System.InvalidCastException 

Czego mi brakuje?

+0

zrób to jak ten var c = (Wiersz ) a; – Ehsan

+1

Nie jestem pewien, czego się spodziewasz - w twoim przykładzie nie ma operatora 'Wiersz (obiekt o)' ... –

+0

Może to być spowodowane tym, że jawny operator oczekuje 'Wiersza ', podczas gdy ty nadajesz mu "obiekt". –

Odpowiedz

4

Problem polega na tym, że odlew nie szuka operatora konwersji, chyba że jeden jest określona na statyczny typ wartości, którą próbujesz rzucić. W twoim przykładzie statyczny typ c to object i object ani nie pochodzi z operatora konwersji ani nie ma go na Row<object>, co powoduje wyjątek środowiska wykonawczego.

Wygląda na to, że ten problem można łatwo obejść dzięki lepszemu projektowi.

Chcesz traktować każdy typ Row<T> jako Row<object>, a operator konwersji nie robi nic więcej niż obejście faktu, że typy te nie są powiązane hierarchicznie. Dlaczego więc nie zrobić z nimi powiązania i uniknąć problemu?

Na przykład:

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; } 
    } 
} 

To wydaje się robić to, co chcesz:

  • Odlew problem jest rozwiązany, ponieważ można teraz rzucać każdy rodzaj Row<T> do klasy bazowej (co trwa nad obowiązkami Row<object> w początkowym projekcie) i łatwo uzyskać dostęp do Name i Value, bez względu na rodzaj, który jest Value.
  • Setter Row.Value jest chroniony, więc nie można rzucić Row<int> do i utworzyć Value np. a string z zewnątrz, zachowując bezpieczeństwo typu.
+0

Wow, po prostu testowanie tego projektu, początkowo wygląda na to, że rzeczywiście rozwiązuje główną przyczynę (nie tak wspaniały projekt). Potrzebujesz zamówić książkę Jona Skeeta ... – Phil

6

Zastosowanie dynamic zamiast object zmusić wykonawczego Real Sprawdź typ:

var c = (dynamic)a; 
var d = (Row<object>)c; // Works fine 

To będzie zadzwonić do operatora Row<T> -> Row<object> plastikowymi.

+1

Nie można zadeklarować 'jawnego operatora Row (obiekt o)'. –

+0

@AdamHouldsworth To prawda. – MarcinJuraszek

+0

Visual Studio narzeka ... więc powinienem najpierw rzucić odbicie obiektu wraca do dynamicznego, a następnie do mojego typu? – Phil

2

Można to osiągnąć z refleksji:

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) 
     }; 
    } 
} 

nazwałbyś to z:

var d = RowHelper.LoadRow(c);