我正在寻找一种方法来避免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。
答案 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);
}
}
使用类型int
和Expression.Assign
的另一个参数,以类似的方式构造setter。生成的lambda将编译为Action<Test,int>
而不是Func<Test,int>
,因为setter不返回值。
答案 1 :(得分:0)
你说,你不能使用动态代码生成。以下是其他一些想法: