Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using FieldInfo.SetValue with a DynamicObject as argument 2

Tags:

dynamic

c#-4.0

I ran into a problem today when trying to set a field using FieldInfo.SetValue() passing a DynamicObject as the second argument. In my case, the field is a Guid and the DynamicObject should be able to convert itself to a one (using TryConvert) but it fails with an ArgumentException.

Some code that shows the problem:

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

I'm not very familiar with the whole dynamicness of C# 4, but this felt to me like something that should work.. What am I doing wrong? Is there another way of doing this?

like image 516
CodingInsomnia Avatar asked Feb 16 '26 23:02

CodingInsomnia


1 Answers

No, this shouldn't work - because the dynamic portion ends where your code ends. The compiler is calling a method with a signature of

void SetValue(Object obj, Object value)

That method call is dynamic, but it's just going to end up passing in a reference to the instance of MyDynamicObj. The call is resolved at execution time, but nothing in SetValue knows anything about the dynamic nature of the object whose reference you're passing in.

Basically you need to perform the dynamic part (the conversion in this case) in your code - the bit that involves the C# 4 compiler doing all its tricks. You've got to perform that conversion, and then you can call SetField.

To put it another way - it's a bit like calling SetField with a field of type XName, but passing in a string. Yes, there's a conversion from string to XName, but it's not SetField's job to work that out. That's the compiler's job.

Now, you can get this to work by making the compiler do some of the work, but you still need to do some with reflection:

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;
}
like image 98
Jon Skeet Avatar answered Feb 20 '26 03:02

Jon Skeet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!