运行时未知枚举类型如何创建返回其底层int值的get/set访问委托
解决方案
错误原因
你遇到的报错是因为CLR要求Delegate.CreateDelegate创建的委托签名必须与属性的get/set方法签名完全匹配,枚举类型和其底层int属于不同的类型,不允许直接隐式转换委托类型。
实现方案
方案1:委托包装(易维护实现)
通过创建类型匹配的枚举委托,再加一层转换逻辑得到int类型的访问委托,实现简单兼容性好:
public static class DelegateHelper { public static (Func<int> getter, Action<int> setter) CreateEnumAccessors(object instance, PropertyInfo property) { if (!property.PropertyType.IsEnum) throw new ArgumentException("属性必须为枚举类型"); var genericMethod = typeof(DelegateHelper) .GetMethod(nameof(CreateEnumAccessorsGeneric), BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(property.PropertyType); return ((Func<int>, Action<int>))genericMethod.Invoke(null, new[] { instance, property }); } private static (Func<int> getter, Action<int> setter) CreateEnumAccessorsGeneric<TEnum>(object instance, PropertyInfo property) where TEnum : Enum { // 先创建类型完全匹配的枚举委托 var getEnum = (Func<TEnum>)Delegate.CreateDelegate(typeof(Func<TEnum>), instance, property.GetGetMethod()); var setEnum = (Action<TEnum>)Delegate.CreateDelegate(typeof(Action<TEnum>), instance, property.GetSetMethod()); // 加转换层得到int类型委托 Func<int> getter = () => Convert.ToInt32(getEnum()); Action<int> setter = val => setEnum((TEnum)Enum.ToObject(typeof(TEnum), val)); return (getter, setter); } }
调用方式如下:
var (getterDelegate, setterDelegate) = DelegateHelper.CreateEnumAccessors(arg, property); settingObj = new MultipleChoice(multipleChoiceAttribute, getterDelegate, setterDelegate, property.PropertyType);
方案2:表达式树编译(高性能实现)
适合需要批量创建大量委托的场景,性能比委托包装高30%以上:
public static (Func<int> getter, Action<int> setter) CreateEnumAccessorsByExpr(object instance, PropertyInfo property) { if (!property.PropertyType.IsEnum) throw new ArgumentException("属性必须为枚举类型"); var instanceExpr = Expression.Constant(instance); var propertyExpr = Expression.Property(instanceExpr, property); // 构造getter: 枚举值转int var getConvertExpr = Expression.Convert(propertyExpr, typeof(int)); var getter = Expression.Lambda<Func<int>>(getConvertExpr).Compile(); // 构造setter: int转枚举后赋值 var valParam = Expression.Parameter(typeof(int), "val"); var setConvertExpr = Expression.Convert(valParam, property.PropertyType); var assignExpr = Expression.Assign(propertyExpr, setConvertExpr); var setter = Expression.Lambda<Action<int>>(assignExpr, valParam).Compile(); return (getter, setter); }
内容的提问来源于stack exchange,提问作者EmmetOT
相关产品推荐
相关产品推荐

