Использование FieldInfo.SetValue с DynamicObject в качестве аргумента 2 - PullRequest
3 голосов
/ 04 ноября 2011

Я столкнулся с проблемой сегодня, когда пытался установить поле, используя FieldInfo.SetValue(), передавая DynamicObject в качестве второго аргумента.В моем случае это поле Guid и DynamicObject должно быть в состоянии преобразовать себя в единицу (с помощью TryConvert), но оно не работает с ArgumentException.

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

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

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

Ответы [ 2 ]

4 голосов
/ 04 ноября 2011

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

void SetValue(Object obj, Object value)

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

В основном вам нужно выполнить динамическую часть (преобразование в этомcase) в вашем коде - бит, в котором компилятор 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 голосов
/ 04 ноября 2011

Вам нужно явное приведение, чтобы вызвать 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...