2
다음 struct
에서 값을 검색하려고합니다.struct에 대해 IL을 사용하여 속성 값 가져 오기
public struct MyType
{
public string Key { get; set; }
public string Value { get; set; }
}
다음 코드를 사용하십시오.
var mem = new MemberAccessor(typeof(MyType), "Value");
var r = new MyType {Key = "One", Value = "Two"};
object s = mem.Get(r);
Console.WriteLine(s); //Should be Two but is One
그리고 그것은 작동 하지만 그것은 나에게 "키"없습니다 "값"의 값을 제공합니다. "키"값을 얻으려고하면 "메모리에서 읽을 수 없습니다"라는 오류 메시지가 표시됩니다. 오류.
위 코드에서 사용 된 대리자는 다음과 같습니다. 이 문제는 struct
일 경우에만 발생합니다.
public class MemberAccessor
{
private readonly Type _targetType;
private readonly Type _memberType;
private readonly MemberInfo _member;
private static readonly Hashtable _mTypeHash = new Hashtable
{
[typeof(sbyte)] = OpCodes.Ldind_I1,
[typeof(byte)] = OpCodes.Ldind_U1,
[typeof(char)] = OpCodes.Ldind_U2,
[typeof(short)] = OpCodes.Ldind_I2,
[typeof(ushort)] = OpCodes.Ldind_U2,
[typeof(int)] = OpCodes.Ldind_I4,
[typeof(uint)] = OpCodes.Ldind_U4,
[typeof(long)] = OpCodes.Ldind_I8,
[typeof(ulong)] = OpCodes.Ldind_I8,
[typeof(bool)] = OpCodes.Ldind_I1,
[typeof(double)] = OpCodes.Ldind_R8,
[typeof(float)] = OpCodes.Ldind_R4
};
public static Type GetMemberInfoType(MemberInfo member)
{
Type type;
if (member is FieldInfo)
type = ((FieldInfo)member).FieldType;
else if (member is PropertyInfo)
type = ((PropertyInfo)member).PropertyType;
else if (member == null)
type = typeof(object);
else
throw new NotSupportedException();
return type;
}
/// <summary>
/// Creates a new property accessor.
/// </summary>
/// <param name="targetType">Target object type.</param>
/// <param name="memberName">Property name.</param>
public MemberAccessor(Type targetType, string memberName)
{
_targetType = targetType;
MemberInfo memberInfo = (targetType).GetProperties().First(x => x.Name == memberName);
if (memberInfo == null)
{
throw new Exception(string.Format("Property \"{0}\" does not exist for type " + "{1}.", memberName, targetType));
}
var canRead = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanRead;
var canWrite = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanWrite;
// roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
if (!canWrite)
{
var backingFieldName = $"<{memberName}>k__BackingField";
var backingFieldMemberInfo = targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == backingFieldName);
if (backingFieldMemberInfo != null)
{
memberInfo = backingFieldMemberInfo;
canWrite = true;
}
}
_memberType = GetMemberInfoType(memberInfo);
_member = memberInfo;
if (canWrite)
{
SetDelegate = GetSetDelegate();
}
if (canRead)
{
GetDelegate = GetGetDelegate();
}
}
private Func<object, object> GetDelegate = null;
private Action<object, object> SetDelegate = null;
/// <summary>
/// Sets the property for the specified target.
/// </summary>
/// <param name="target">Target object.</param>
/// <param name="value">Value to set.</param>
public void Set(object target, object value)
{
SetDelegate?.Invoke(target, value);
}
public object Get(object target)
{
return GetDelegate?.Invoke(target);
}
private Action<object, object> GetSetDelegate()
{
Type[] setParamTypes = new Type[] { typeof(object), typeof(object) };
Type setReturnType = null;
var owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var setMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
//
ILGenerator setIL = setMethod.GetILGenerator();
//
// Emit the IL.
//
Type paramType = _memberType;
setIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
setIL.Emit(OpCodes.Castclass, this._targetType);
setIL.Emit(OpCodes.Ldarg_1); //Load the second argument
//(value object)
if (paramType.GetTypeInfo().IsValueType)
{
setIL.Emit(OpCodes.Unbox, paramType); //Unbox it
if (_mTypeHash[paramType] != null) //and load
{
OpCode load = (OpCode)_mTypeHash[paramType];
setIL.Emit(load);
}
else
{
setIL.Emit(OpCodes.Ldobj, paramType);
}
}
else
{
setIL.Emit(OpCodes.Castclass, paramType); //Cast class
}
if (IsField(_member))
{
setIL.Emit(OpCodes.Stfld, (FieldInfo)_member);
}
else
{
MethodInfo targetSetMethod = GetSetMethodOnDeclaringType(((PropertyInfo)this._member));
if (targetSetMethod != null)
{
setIL.Emit(OpCodes.Callvirt, targetSetMethod);
}
else
{
setIL.ThrowException(typeof(MissingMethodException));
}
}
setIL.Emit(OpCodes.Ret);
var del = setMethod.CreateDelegate(Expression.GetActionType(setParamTypes));
return del as Action<object, object>;
}
public static bool IsField(MemberInfo member)
{
return member is FieldInfo;
}
public static MethodInfo GetSetMethodOnDeclaringType(PropertyInfo propertyInfo)
{
var methodInfo = propertyInfo.GetSetMethod(true);
return methodInfo ?? propertyInfo
.DeclaringType
.GetProperty(propertyInfo.Name)
.GetSetMethod(true);
}
private Func<object, object> GetGetDelegate()
{
Type[] setParamTypes = new[] { typeof(object) };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.DeclareLocal(typeof(object));
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
getIL.Emit(OpCodes.Castclass, this._targetType);
//Get the property value
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
if (_memberType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, _memberType);
}
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
getIL.Emit(OpCodes.Callvirt, targetGetMethod);
if (targetGetMethod.ReturnType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, targetGetMethod.ReturnType);
}
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamTypes.Concat(new[] { setReturnType }).ToArray()));
return del as Func<object, object>;
}
}
당신은 [mcve]을 제공시겠습니까? 여기서 정의되지 않은 몇 가지 외부 비트가 있습니다. – DavidG
@DavidG Full 클래스 추가됨 – Schotime