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