对底层字段数据的直接内存访问

时间:2014-01-01 12:25:59

标签: c# pointers unsafe

我正在寻找一种方法来避免FieldInfo.Get / SetValue开销,并直接访问内存以供一些选择,提前知道原始类型。 (更具体地说,我希望避免在我们的自定义序列化程序中进行任何内存分配)

基本上,这是官方允许我做的事情:

System.Object o = someobject;
int inOut = 0;

var type = o.GetType();
var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (var fi in fieldInfos) {
    fi.SetValue(o, inOut);
    inOut = (int)fi.GetValue(o);
}

这就是我想要做的事情:

foreach (var fi in fieldInfos) {
    fixed(int* ip = o.basePointer + fi.fieldOffset) {
        *p = inOut;
        inOut = *p;
    }
}

我只会将它用于Int32,Single和bool。我主要感兴趣的是让它在Mono上工作,所以如果有任何可用的Mono,那就没事了。

注意:我很清楚“你不应该这样做”,“你有没有把它描述”等等。我知道,而且我有,这就是为什么我要调查这个。我们有一个非常具体的案例,我们控制所有变量(和所有代码),但我们希望它可以在任何“普通”类上工作,而不需要额外的标记或显式的结构布局。

编辑:我应该指出,我无法发出动态代码来解决这个问题。我没有解决方案要求我在前面编写和组装IL。

2 个答案:

答案 0 :(得分:2)

  

我很清楚"你不应该这样做"

这很好 - 我将跳过这部分解释,然后直接访问避免内存分配的字段,同时保持在托管代码的限制范围内。

您可以使用LINQ表达式为getter构建Func<ObjType,int>,为setter构建Action<ObjType,int>。调用这些仿函数可以让您获取或设置int字段,就像您直接访问他们的方法一样。

以下是制作无包装吸气剂的方法:

public class Test
{
    public int myfield;
    public static void Main()
    {
        // Make a parameter expression to represent the object
        var argExpr = Expression.Parameter(typeof(Test), "a");
        // Get the field of your object (the same way as in your first example)
        var field = typeof(Test).GetField("myfield", BindingFlags.Public | BindingFlags.Instance);
        // Make an expression accessing the field from the parameter
        var fieldExpr = Expression.Field(argExpr, field);
        // Compile the expression into a functor
        var getter = (Func<Test,int>)Expression.Lambda(fieldExpr, argExpr).Compile();
        // Construct a test object
        var tmp = new Test {myfield = 123};
        // Use a wrapper to avoid "boxing"/"unboxing" of "GetValue"
        int res = getter(tmp);
        Console.WriteLine("Res={0}", res);
    }
}

Demo on ideone.

使用类型intExpression.Assign的另一个参数,以类似的方式构造setter。生成的lambda将编译为Action<Test,int>而不是Func<Test,int>,因为setter不返回值。

答案 1 :(得分:0)

你说,你不能使用动态代码生成。以下是其他一些想法:

  1. 如果您可以使用属性而不是字段,请创建属性getter(https://stackoverflow.com/a/724427)的委托。
  2. 在构建时为序列化程序生成IL代码。将其编译为可在运行时加载的程序集。只需为每个字段生成访问者代码。我认为当FullTrust和SkipVerification权限存在时,您可以访问IL中的私有成员。