在C#中查找与引用参数对应的对象字段

时间:2009-05-14 02:53:13

标签: c# .net reflection concurrency clr

当您使用yield语法在C#中编写迭代器函数时,编译器会在内部将您的函数转换为类。所以,如果我写这样的东西:

IEnumerator<int> MyIterator () {
  for (int i = 0; i < 10; i++)
    yield return i;
}

调用MyIterator()构造一个带有i的私有字段的类,而不是使用堆栈变量。

现在提出问题:我想找到一种方法来定位用于存储i值的字段。原因有点牵扯:

我正在使用迭代器函数进行协作线程。我在迭代器中编写这样的简单代码,以便在异步操作完成之前暂停函数的执行。

Future<string> f;
yield return file.readLine(out f);
string line = f.Result;

执行这三个语句时,将调用readLine函数并将未来存储到'f'字段中。然后暂停迭代器函数,并在'f'Vist已将结果存储到其中时唤醒,此时我可以检索结果。

我希望能够做到的是:

string line;
yield return file.readLine(out line);

在正常情况下,这在.NET中是不可能的,因为没有办法保证堆栈变量保持活动足够长的时间以便我能够写入它。

但是,知道迭代器函数将所有本地存储在字段中,理论上我可以通过保持对迭代器的引用(使其保持活动状态)和字段本身来做到这一点,这样当readLine操作完成时,我可以将结果直接存储到字段中。

问题是CLR似乎没有给我这样做的方法。

如果我有'out'或'ref'参数,我可以使用不安全的C#或原始MSIL将该参数转换为原始指针,引用或TypedReference。不幸的是,将这三种类型中的任何一种存储在字段中是不合法的 - 即使你欺骗编译器让你这样做,字节码验证器也不会让你执行代码。这很可能是因为存储对堆栈变量的引用本质上是不安全的。

所以我要做的是编写一个带有out / ref参数的函数,并搜索调用迭代器的字段以查找相应的字段。一旦有了该字段,它就会将对迭代器和字段的引用存储到包装器类型中,并将包装器与异步操作相关联。

考虑到.NET中运行时反射所涉及的开销,我怀疑如果没有预处理我的源代码或在编译后对程序集进行后处理,这是不可能的。

但是,我很想听到一些替代解决方案的建议。 :)有什么想法吗?

2 个答案:

答案 0 :(得分:1)

使用迭代器字段进行修改是在乞求麻烦。使用您在迭代器签名中指定的类的成员不是更简单吗?将它传入或传出?在这个过程中基本上涉及某种简单的包装类?

class MyWrapper {
    public string Line {get;set;}
}

允许您通过Line对象引用安全地与MyWrapper交谈?

我不明白所有的问题,所以这是我能说的最好的......

答案 1 :(得分:0)

我终于在这里找到了满足我需求的解决方案,你知道什么 - 它涉及到LINQ。

关于.NET的博客文章提到了如何将任何表达式lambda转换为Expression&lt;&gt;对象,如果您知道表达式的结果类型。鉴于此,事实证明你可以采取这样的表达式:

()=&gt; this.Foo.Bar

然后遍历节点以获得MemberInfo(用于'Bar')和'this'对象(用于'this.Foo')。给定这两个值,您可以直接绑定到编译时指定的字段或属性。对于属性,您可以通过从MemberInfo检索属性的Get和Set方法并将它们转换为强类型委托来非常有效地执行此操作,从而使读取/写入该属性的成本非常低。

最重要的是,这与自动重构和源代码搜索完美配合,因为它正在为您提供编译器的功能,并且它不需要对现有代码进行任何修改以允许绑定。

唯一的缺点是绑定到结构的字段/属性非常困难,但我不认为这是一个特别相关的用例。

任何想要解决此问题或类似问题的人都可以在此处查看源代码:

http://code.google.com/p/fracture/source/browse/trunk/Squared/Util/Bind.cs