2011-09-08 4 views
10

Я хочу использовать отражение и делать либо неявное, либо явное coversion с использованием отражения.Как выполнить явное выполнение операции от отражения?

Учитывая я определил Foo таким образом

public class Foo 
{ 
    public static explicit operator decimal(Foo foo) 
    { 
     return foo.Value; 
    } 

    public static explicit operator Foo(decimal number) 
    { 
     return new Foo(number); 
    } 

    public Foo() { } 

    public Foo(decimal number) 
    { 
     Value = number; 
    } 

    public decimal Value { get; set; } 

    public override string ToString() 
    { 
     return Value.ToString(); 
    } 
} 

Когда я запускаю этот код

decimal someNumber = 42.42m; 

var test = (Foo)someNumber; 

Console.WriteLine(test);  // Writes 42.42 No problems 

Когда я пытаюсь определить класс с Foo в качестве типа элемента и использовать отражение, чтобы установить его. Я получаю следующее исключение.

Error  : Object of type 'System.Decimal' cannot be converted to type 'Foo'. 
StackTrace: at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) 
       at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) 
       at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) 
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
       at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) 
       at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) 

Вот код, я использую, чтобы установить свойство с отражением

public class FooComposite 
{ 
    public Foo Bar { get; set; } 
} 

var properties = typeof(FooComposite).GetProperties(); 

var testFoo = new FooComposite(); 

foreach(var propertyInfo in properties) 
{ 
    propertyInfo.SetValue(testFoo, 17.17m, null); // Exception generated on this line 
} 

Console.WriteLine(testFoo.Bar); // Never gets here 

Как я могу сделать это преобразование?

+0

Если Вы желаете сделать это динамически, а не код бросание, Марк сделал что-то очень близко к тому, что вы ищете более на форумах MSDN, используя выражения (прочитать всю дискуссию, а не только ответ). См. Http://social.msdn.microsoft.com/Forums/eu/csharplanguage/thread/c2a77a57-ebbb-4ac1-94c9-5287f01105ff – vcsjones

ответ

5

Ну его на самом деле ничем не отличается от кода, не отражения, вы должны явно привести номер в Foo:

propertyInfo.SetValue(testFoo,(Foo)17.17m, null); 

Живой пример: http://rextester.com/rundotnet?code=BPQ74480

Из интереса я попробовал несколько альтернативы.

  1. Сделать это implicit отлитый в Foo - не работает, та же ошибка Live
  2. Использование Convert.ChangeType(17.17m,typeof(Foo)) - также не работает. Live
+0

Это хорошее сообщение в блоге, в котором больше говорится об этой проблеме бокса и распаковки. http://philosopherdeveloper.wordpress.com/2010/05/05/the-difference-between-converting-and-unboxing-read-my-mind-compiler/ –

4

Я посмотрел на этот вопрос сегодня при попытке скопировать поля по имени между объектами. Я был очень разочарован, увидев, что выбранным ответом было «вы можете явно называть явный оператор». В конце концов, все остальное может быть сделано путем отражения.

Моя проблема была методом отражения, пытающимся сделать глубокую копию между двумя классами из-за сложного типа. Я попытался определить преобразование explicit operator, но, похоже, оно не получилось вызвано, поэтому я понял способ получить его путем отражения. Используя некоторые другие исследования о вызове статических методов, я нашел, что это работает для меня при копировании сложного типа, хранящегося в pSource, в другой тип в свойстве pDest. тип pDest имеет преобразование из типа pSource.

dstVar - мой экземпляр адресата. pDest - это текущий PropertyInfo в экземпляре адресата.

источник - это мой экземпляр источника. pSource - это текущий PropertyInfo в исходном экземпляре.

Тип используется для моего имущества назначения имеет явное преобразование типа свойства источника, это работает без необходимости каких-либо

4

мне нужна функциональность как Ted H, но я реализовал это следующим образом:

var cast = typeof(dest).GetMethod("op_Explicit", new Type[] { typeof(source) }); 
var result = cast.Invoke(null, new object[] {value}); 

Редактировать: В последнее время мне нужна была более развитая версия, и это то, что я придумал. Имейте в виду, что он не охватывает все доступные конверсии.

private static object DynamicCast(object source, Type destType) { 
    Type srcType = source.GetType(); 
    if (srcType == destType) return source; 

    var paramTypes = new Type[] { srcType }; 
    MethodInfo cast = destType.GetMethod("op_Implicit", paramTypes); 

    if (cast == null) { 
     cast = destType.GetMethod("op_Explicit", paramTypes); 
    } 

    if (cast != null) return cast.Invoke(null, new object[] { source }); 

    if (destType.IsEnum) return Enum.ToObject(destType, source); 

    throw new InvalidCastException(); 

} 
+0

Обратите внимание, что как исходный, так и целевой типы могут допускать преобразование операторы. Так, например, если у вас есть явное преобразование из A в B, то это может быть определено в классе A или классе B или в обоих. –

+0

Думаю, я мог бы реализовать это как это намеренно. Возможно, так, что он соответствует тому, как он работает в статическом коде C#, но я не уверен в этом. Если вы действительно хотите сделать обширные преобразования, вы также можете посмотреть в класс 'Convert' и' TypeConverter'. – Herman

1

Опираясь на Herman's answer ... Я понял, что источник и класс назначения может определить оператор преобразования. Так вот моя версия:

private static bool DynamicCast(object source, Type destType, out object result) 
{ 
    Type srcType = source.GetType(); 
    if (srcType == destType) { result = source; return true; } 
    result = null; 

    BindingFlags bf = BindingFlags.Static | BindingFlags.Public; 
    MethodInfo castOperator = destType.GetMethods(bf) 
           .Union(srcType.GetMethods(bf)) 
           .Where(mi => mi.Name == "op_Explicit" || mi.Name == "op_Implicit") 
           .Where(mi => 
           { 
            var pars = mi.GetParameters(); 
            return pars.Length == 1 && pars[0].ParameterType == srcType; 
           }) 
           .Where(mi => mi.ReturnType == destType) 
           .FirstOrDefault(); 
    if (castOperator != null) result = castOperator.Invoke(null, new object[] { source }); 
    else return false; 
    return true; 
} 

Типичное использование:

object a = new A(); 
object o; 
if (DynamicCast(a, typeof(B), out o)) 
{ 
    B b = (B)o; 
    ... 
} 

Обратите внимание на следующее:

  • Если преобразование определяется как источника и назначения, операторный метод преобразования назначения заданный приоритет
  • Функция возвращает bool с указанием успеха/отказа, а фактическое преобразованное значение в out переменная (аналогична методам TryParse)
0

Спасибо всем, что было сделано для того, чтобы начать то, что мне нужно. Я позаимствовал и добавил. В моей ситуации мне понадобилось все вышеперечисленное, а также нужно было искать все базовые типы предков как для источника, так и для целевого типа, чтобы узнать, содержит ли какое-либо из них неявное или явное преобразование в мои целевые типы. Добавив это дополнительное требование, я привел ниже.

private static bool TryCast(object source, Type destType, out object result) 
    { 
     Type srcType = source.GetType(); 
     if (srcType == destType) 
     { 
      result = source; 
      return true; 
     } 

     MethodInfo cast = null; 
     while (cast == null && srcType != typeof(object)) 
     { 
      cast = GetCastMethod(srcType, srcType, destType); 
      if (cast == null) cast = GetCastMethod(destType, srcType, destType); 
      srcType = srcType.BaseType; 
     } 

     if (cast != null) 
     { 
      result = cast.Invoke(null, new object[] { source }); 
      return true; 
     } 

     if (destType.IsEnum) 
     { 
      result = Enum.ToObject(destType, source); 
      return true; 
     } 

     result = null; 
     return false; 
    } 

    private static MethodInfo GetCastMethod(Type typeWithMethod, Type srcType, Type destType) 
    { 
     while (typeWithMethod != typeof(object)) 
     { 
      foreach (MethodInfo method in typeWithMethod.GetMethods(BindingFlags.Static | BindingFlags.Public)) 
      { 
       if (method.ReturnType == destType && (method.Name == "op_Explicit" || method.Name == "op_Implicit")) 
       { 
        ParameterInfo[] parms = method.GetParameters(); 
        if (parms != null && parms.Length == 1 && parms[0].ParameterType == srcType) 
         return method; 
       } 
      } 
      typeWithMethod = typeWithMethod.BaseType; 
     } 

     return null; 
    }