实际上,我应该问:我怎么能和保持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
的通用对应物不存在。有没有其他方法通过反射设置(引用a)结构的字段?
答案 0 :(得分:6)
对于属性,如果您具有struct和property类型,则可以从属性setter创建委托。正如您所指出的,字段没有setter,但您可以创建一个行为完全相同的字段:
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);
}
}
答案 1 :(得分:5)
在SetValueDirect上创建符合cls的包装器:
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 :(得分:2)
不确定这是否适合您的约束,但通过将结构实例声明为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
有关详情,请参阅this answer。