C# 为什么TypedReference.MakeTypedReference如此受限?
我终于理解了这个方法的用法,但是为什么参数如此有限呢?基本的私有C# 为什么TypedReference.MakeTypedReference如此受限?,c#,interop,clr,typedreference,C#,Interop,Clr,Typedreference,我终于理解了这个方法的用法,但是为什么参数如此有限呢?基本的私有InternalMakeTypedReference(void*result,object target,IntPtr[]flds,RuntimeType lastFieldType)比MakeTypedReference可以做更多的事情,后者限制字段数组具有元素,字段类型为非基本类型 我制作了一个示例使用代码,显示了它的全部可能性: private static readonly MethodInfo InternalMakeTy
InternalMakeTypedReference(void*result,object target,IntPtr[]flds,RuntimeType lastFieldType)
比MakeTypedReference
可以做更多的事情,后者限制字段数组具有元素,字段类型为非基本类型
我制作了一个示例使用代码,显示了它的全部可能性:
private static readonly MethodInfo InternalMakeTypedReferenceMethod = typeof(TypedReference).GetMethod("InternalMakeTypedReference", flags);
private static readonly Type InternalMakeTypedReferenceDelegateType = ReflectionTools.NewCustomDelegateType(InternalMakeTypedReferenceMethod.ReturnType, InternalMakeTypedReferenceMethod.GetParameters().Select(p => p.ParameterType).ToArray());
private static readonly Delegate InternalMakeTypedReference = Delegate.CreateDelegate(InternalMakeTypedReferenceDelegateType, InternalMakeTypedReferenceMethod);
public static void MakeTypedReference([Out]TypedReference* result, object target, params FieldInfo[] fields)
{
IntPtr ptr = (IntPtr)result;
IntPtr[] flds = new IntPtr[fields.Length];
Type lastType = target.GetType();
for(int i = 0; i < fields.Length; i++)
{
var field = fields[i];
if(field.IsStatic)
{
throw new ArgumentException("Field cannot be static.", "fields");
}
flds[i] = field.FieldHandle.Value;
lastType = field.FieldType;
}
InternalMakeTypedReference.DynamicInvoke(ptr, target, flds, lastType);
}
那么,为什么开发人员似乎毫无意义地决定不允许这种使用,而这显然是有用的呢?责备和他该死的“类型理论”
这是任何ref
(托管指针)引用(包括新的C#7特性)的固有特性,正如您所观察到的,您可以将其用于读写目标。因为这不是重点吗
现在,由于无法排除这两种可能性,强类型要求在类型层次结构中从上到下约束每个ref
的Type
更正式地说,类型
被限制为多态性的交叉点,否则它将符合该交叉点的条件。显然,这个交集的结果会折叠为一个类型
,它本身是不变的
那么,为什么开发人员似乎毫无意义地决定不允许这种使用,而这显然是有用的呢
因为如果我们有字段,就不需要它
您在这里以一种非常复杂的方式进行的工作基本上如下所示:
((Int32)a).m_value = 1;
当然,在纯C#中,我们不能这样做,因为类((点)p).X=1
赋值在CS0445中失败:无法修改取消装箱转换的结果
更不用说Int32.m_value
是int,这也是Int32
结构。不能在C#:CS0523中创建这样的值类型:Struct member会导致结构布局中出现循环
MakeTypedReference
实际上返回了FieldInfo
的TypedReference
。稍微干净一点的无限制变体可能是:
// If target contains desiredField, then returns it as a TypedReference,
// otherwise, returns the reference to the last field
private static unsafe void MakeTypedReference(TypedReference* result, object target, FieldInfo desiredField = null)
{
var flds = new List<IntPtr>();
Type lastType = target.GetType();
foreach (FieldInfo f in target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
flds.Add(f.FieldHandle.Value);
lastType = f.FieldType;
if (f == desiredField)
break;
}
InternalMakeTypedReference.DynamicInvoke((IntPtr)result, target, flds.ToArray(), lastType);
}
如果确实要使用类型引用
(不带反射API),则可以直接在对象上使用它,然后通过此引用访问装箱值。您只需要知道托管对象引用的内存布局:
但实际上,这只是比
FieldInfo.SetValue
版本快一点,主要是由于对象固定。我不理解您的实际问题。我认为底层代码是伪代码,因为您试图为方法设置一个值。你想这么做吗?不要在伪。。。。我刚刚发现\uuu refvalue
是一个未记录的关键字。@TyCobb不,这只是一个理论问题,为什么一些功能没有完全实现。一切正常。
// If target contains desiredField, then returns it as a TypedReference,
// otherwise, returns the reference to the last field
private static unsafe void MakeTypedReference(TypedReference* result, object target, FieldInfo desiredField = null)
{
var flds = new List<IntPtr>();
Type lastType = target.GetType();
foreach (FieldInfo f in target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
flds.Add(f.FieldHandle.Value);
lastType = f.FieldType;
if (f == desiredField)
break;
}
InternalMakeTypedReference.DynamicInvoke((IntPtr)result, target, flds.ToArray(), lastType);
}
object a = 98;
FieldInfo int32mValue = typeof(int).GetTypeInfo().GetDeclaredField("m_value");
int32mValue.SetValue(a, 1);
Console.WriteLine(a); // 1
object a = 98;
// pinning is required to prevent GC reallocating the object during the pointer operations
var objectPinned = GCHandle.Alloc(a, GCHandleType.Pinned);
try
{
TypedReference objRef = __makeref(a);
// objRef.Value->object->boxed content
int* rawContent = (int*)*(IntPtr*)*(IntPtr*)&objRef;
// A managed object reference points to the type handle
// (that is another pointer to the method table), which is
// followed by the first field.
if (IntPtr.Size == 4)
rawContent[1] = 1;
else
rawContent[2] = 1;
}
finally
{
objectPinned.Free();
}
Console.WriteLine(a); // 1