Могу ли я установить значение для структуры с помощью отражения без бокса? - PullRequest
9 голосов
/ 29 марта 2012

На самом деле, я должен был спросить: как я могу сделать это и , чтобы соответствовать CLS? Потому что я могу думать только о том, чтобы сделать это следующим образом, но использование __makeref, FieldInfo.SetValueDirect или просто System.TypedReference в целом делает недействительным соответствие CLS.

// code illustrating the issue:
TestFields fields = new TestFields { MaxValue = 1234 };  // test struct with one field

FieldInfo info = fields.GetType().GetField("MaxValue");  // get the FieldInfo

// actual magic, no boxing, not CLS compliant:
TypedReference reference = __makeref(fields);
info.SetValueDirect(reference, 4096);

Соответствующий аналог SetValueDirect равен SetValue, но он принимает объект в качестве цели, поэтому моя структура будет упакована, что заставит меня установить значение для копии, а не исходной переменной.

Общий аналог для SetValue, насколько я знаю, не существует. Есть ли другой способ задания поля (ссылки на) структуры с помощью отражения?

Ответы [ 3 ]

6 голосов
/ 29 марта 2012

Для свойств, если у вас есть структура и типы свойств, вы можете создать делегата из установщика свойств.Как вы указали, у полей нет сеттеров, но вы можете создать тот, который ведет себя точно так же:

delegate void RefAction<T1, T2>(ref T1 arg1, T2 arg2);

struct TestFields
{
    public int MaxValue;

    public int MaxValueProperty
    {
        get { return MaxValue; }
        set { MaxValue = value; }
    }
};

static class Program
{
    static void Main(string[] args)
    {
        var propertyInfo = typeof(TestFields).GetProperty("MaxValueProperty");
        var propertySetter = (RefAction<TestFields, int>)Delegate.CreateDelegate(typeof(RefAction<TestFields, int>), propertyInfo.GetSetMethod());

        var fieldInfo = typeof(TestFields).GetField("MaxValue");

        var dynamicMethod = new DynamicMethod(String.Empty, typeof(void), new Type[] { fieldInfo.ReflectedType.MakeByRefType(), fieldInfo.FieldType }, true);
        var ilGenerator = dynamicMethod.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Stfld, fieldInfo);
        ilGenerator.Emit(OpCodes.Ret);
        var fieldSetter = (RefAction<TestFields, int>)dynamicMethod.CreateDelegate(typeof(RefAction<TestFields, int>));

        var fields = new TestFields { MaxValue = 1234 };
        propertySetter(ref fields, 5678);
        fieldSetter(ref fields, 90);
        Console.WriteLine(fields.MaxValue);
    }
}
5 голосов
/ 29 марта 2012

Создание CLS-совместимой оболочки в SetValueDirect:

  var item = new MyStruct { X = 10 };

  item.GetType().GetField("X").SetValueForValueType(ref item, 4);


[CLSCompliant(true)]
static class Hlp
{
  public static void SetValueForValueType<T>(this FieldInfo field, ref T item, object value) where T : struct
  {
    field.SetValueDirect(__makeref(item), value);
  }
}
2 голосов
/ 29 марта 2012

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

    ValueType fields = new TestFields { MaxValue = 1234 };  // test struct with one field
    FieldInfo info = typeof(TestFields).GetField("MaxValue");  // get the FieldInfo
    info.SetValue(fields, 4096);
    Console.WriteLine(((TestFields)fields).MaxValue);  // 4096

См. этот ответ для получения дополнительной информации.

...