2011-11-03 1 views
3

Сегодня я столкнулся с проблемой при попытке установить поле с использованием FieldInfo.SetValue(), передав DynamicObject в качестве второго аргумента. В моем случае поле является Guid, а DynamicObjectдолжно иметь возможность конвертировать себя в один (с использованием TryConvert), но с ошибкой ArgumentException.Использование FieldInfo.SetValue с DynamicObject как аргумент 2

Некоторый код, который показывает проблему:

// Simple impl of a DynamicObject to prove point 
public class MyDynamicObj : DynamicObject 
{ 
    public override bool TryConvert(ConvertBinder binder, out object result) 
    { 
     result = null; 
     // Support converting this to a Guid 
     if (binder.Type == typeof(Guid)) 
     { 
      result = Guid.NewGuid(); 
      return true; 
     } 
     return false; 
    } 
} 

public class Test 
{ 
    public Guid MyField; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic myObj = new MyDynamicObj(); 

     // This conversion works just fine 
     Guid guid = myObj; 

     var test = new Test(); 
     var testField = typeof(Test).GetField("MyField"); 

     // This, however, fails with: 
     // System.ArgumentException 
     // Object of type 'ConsoleApplication1.MyDynamicObj' cannot be converted to type 'System.Guid'. 
     testField.SetValue(test, myObj); 
    } 
} 

Я не очень хорошо знакомы со всем dynamicness в C# 4, но это чувствовал, мне как-то, что должно работать .. Что я делаю неправильно? Есть ли другой способ сделать это?

ответ

4

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

void SetValue(Object obj, Object value) 

Этот метод вызова является динамичным, но это только будет в конечном итоге передавая ссылку на экземпляр MyDynamicObj. Вызов разрешается во время выполнения, но ничего в SetValue ничего не знает о динамическом характере объекта, ссылка на который вы передаете.

В принципе, вам необходимо выполнить динамическую часть (преобразование в этом случае) в вашем code - бит, который включает компилятор C# 4, который выполняет все свои трюки. Вы должны выполнить это преобразование, и затем вы можете позвонить SetField.

Иными словами, это похоже на вызов SetField с полем типа XName, но передача в строку. Да, есть конверсия от string до XName, но это не работа SetField, чтобы справиться с этим. Это работа компилятора.

Теперь вы можете получить эту работу, сделав компилятор сделать некоторые работы, но вам все еще нужно, чтобы сделать некоторые с отражением:

static void Main(string[] args) 
{ 
    dynamic myObj = new MyDynamicObj(); 

    var test = new Test(); 
    var testField = typeof(Test).GetField("MyField"); 

    var method = typeof(Program) 
     .GetMethod("Convert", BindingFlags.Static | BindingFlags.NonPublic); 
    method = method.MakeGenericMethod(testField.FieldType); 

    object converted = method.Invoke(null, new object[] {myObj}); 
    testField.SetValue(test, converted); 
} 

static T Convert<T>(dynamic input) 
{ 
    return input; 
} 
+0

Ouch. Конечно. * шлепает лоб * – CodingInsomnia

+0

Итак, каким-то образом вокруг него? Могу ли я каким-либо образом вызвать преобразование, учитывая, что я знаю только тип во время выполнения? – CodingInsomnia

+0

@CodingInsomnia: Смотрите мое редактирование :) –

0

Вам нужно явное приведение вызвать TryConvert:

testField.SetValue(test, (Guid)myObj); 

Не уверен, что это то, что вам нужно. Возможно, есть какой-то способ задумчиво сказать: ((DynamicObject)myObj).TryConvert(/*reflected destination type here*/, result)

Другие попытки, которые потерпели неудачу, некоторые из них требуют реализации таких вещей, как определенный интерфейс, поэтому они в основном не используют TryConvert, но, возможно, альтернативный способ выполнить то, что вы хотите:

Type secondType = testField.FieldType; 

    TypeConverter tc = TypeDescriptor.GetConverter(typeof(MyDynamicObj)); 
    object secondObject = tc.ConvertTo(myObj,typeof(Guid)); 
    //var secondObject = Convert.ChangeType(myObj, secondType);//Activator.CreateInstance(secondType); 
    //secondObject = myObj; 
    testField.SetValue(test, secondObject); 
+0

Да, в «реальном» сценарии я знаю только тип во время выполнения, поэтому мне нужно что-то вроде вызова TryConvert, о котором вы говорили. Пробовал что-то подобное, но не мог заставить его бежать. – CodingInsomnia

Смежные вопросы