确定给定方法是读取还是写入成员变量或属性的最简单方法是什么? 我正在编写一个工具来协助RPC系统,其中对远程对象的访问是昂贵的。能够检测方法中是否未使用给定对象可以允许我们避免序列化其状态。在源代码上执行它是完全合理的(但是能够在编译的代码上执行它将是惊人的)
我想我可以编写自己的简单解析器,我可以尝试使用现有的C#解析器之一并使用AST。我不确定是否可以使用Reflection使用Assemblies执行此操作。还有其他方法吗?什么是最简单的?
编辑:感谢所有快速回复。让我提供一些更多信息,使问题更清楚。我当然更喜欢正确,但绝对不应该非常复杂。我的意思是我们不能过分检查极端或不可能的事情(如提到的传入的代表,这是一个很好的观点)。只需要检测这些情况并假设一切都可以使用而不是在那里进行优化就足够了。我认为这些案件相对不常见。 我们的想法是将这个工具交给我们团队以外的开发人员,不应该关注这个优化。该工具获取其代码并为我们自己的RPC协议生成代理。 (我们只使用protobuf-net进行序列化,但没有使用wcf和.net远程处理)。出于这个原因,我们使用的任何东西都必须是免费的,否则我们将无法部署该工具以解决许可问题。
答案 0 :(得分:6)
您可以简单或者您可以更正 - 您更喜欢哪个?
最简单的方法是解析类和方法体。然后标识作为类的属性和字段名称的标记集。出现在方法体中的那些标记的子集是您关心的属性和字段名称。
这个微不足道的分析当然不是正确的。如果你有
class C
{
int Length;
void M() { int x = "".Length; }
}
然后你会错误地断定M引用C.Length。这是误报。
正确的方法是编写一个完整的C#编译器,并使用其语义分析器的输出来回答您的问题。这就是IDE如何实现“转到定义”等功能。
答案 1 :(得分:2)
在尝试自己编写此类逻辑之前,我会检查您是否可以利用NDepend来满足您的需求。
NDepend是一个代码依赖性分析工具......等等。它实现了一个复杂的分析器,用于检查代码结构之间的关系,并且应该能够回答这个问题。如果我没弄错的话,它也可以在源和IL上运行。
NDepend公开CQL - 代码查询语言 - 它允许您针对代码中结构之间的关系编写类似SQL的查询。 NDepend对脚本有一些支持,并且能够与您的构建过程集成。
答案 2 :(得分:2)
要完成关于NDepend的LBushkin答案(免责声明:我是此工具的开发者之一),NDepend确实可以为您提供帮助。下面的Code LINQ Query (CQLinq)实际上匹配了......
的方法请注意我们首先定义4套: typesRPC , fieldsRPC , propertiesRPC , methodsThatShouldntUseRPC - 然后我们匹配违反规则的方法。当然,需要调整此CQLinq规则以匹配您自己的 typesRPC 和 methodsThatShouldntUseRPC :
warnif count > 0
// First define what are types whose call are RDC
let typesRPC = Types.WithNameIn("MyRpcClass1", "MyRpcClass2")
// Define instance fields of RPC types
let fieldsRPC = typesRPC.ChildFields()
.Where(f => !f.IsStatic).ToHashSet()
// Define instance properties getters and setters of RPC types
let propertiesRPC = typesRPC.ChildMethods()
.Where(m => !m.IsStatic && (m.IsPropertyGetter || m.IsPropertySetter))
.ToHashSet()
// Define methods that shouldn't provoke RPC calls
let methodsThatShouldntUseRPC =
Application.Methods.Where(m => m.NameLike("XYZ"))
// Filter method that should do any RPC call
// but that is using any RPC fields (reading or writing) or properties
from m in methodsThatShouldntUseRPC.UsingAny(fieldsRPC).Union(
methodsThatShouldntUseRPC.UsingAny(propertiesRPC))
let fieldsRPCUsed = m.FieldsUsed.Intersect(fieldsRPC )
let propertiesRPCUsed = m.MethodsCalled.Intersect(propertiesRPC)
select new { m, fieldsRPCUsed, propertiesRPCUsed }
答案 3 :(得分:1)
我的直觉是检测哪些成员变量将被访问是错误的方法。我对这种方法的第一个猜测就是根据需要请求序列化对象(最好是在任何函数需要它们的开头,而不是零碎的)。请注意,TCP / IP(即Nagle算法)应该将这些请求填充在一起,如果它们是快速连续制作的并且很小
答案 4 :(得分:0)
通过RPC你的意思是.NET Remoting?还是DCOM?还是WCF?
所有这些都提供了通过接收器和其他构造监视跨进程通信和序列化的机会,但它们都是特定于平台的,因此您需要指定平台......
答案 5 :(得分:0)
您可以使用类似于INotifyPropertyChanged的界面侦听正在读取/写入属性的事件(尽管您显然不知道哪个方法影响了读/写。)
答案 6 :(得分:0)
Eric说得对:要做得好,你需要的是编译器前端。他没有充分强调的是需要强大的流量分析功能(或者愿意接受可能通过用户注释缓解的非常保守的答案)。也许他的意思是“语义分析”这个短语,尽管他的“goto定义”的例子只需要一个符号表,而不是流量分析。
普通的C#解析器只能用于获取非常保守的答案(例如,如果C类中的方法A包含标识符 X,则假设它读取类成员 X;如果A包含否,则您知道它无法读取成员X)。
除此之外的第一步是拥有编译器的符号表和类型信息(如果方法A直接引用类成员 X,则假设它读取成员X;如果A包含** no *调用并且仅在访问不属于此类类型的对象的上下文中提及标识符 X,然后您知道它无法读取成员X)。你也要担心合格的引用;如果Q与C兼容,Q.X可以读取成员X。
粘性点是调用,可以隐藏任意操作。基于解析和符号表的分析可以确定,如果存在调用,则参数仅将引用到常量或不是A可能表示的类(可能是继承的)的对象。
如果您发现具有C兼容类类型的参数,现在您必须确定该参数是否可以绑定到此,需要进行控制和数据流分析:
method A( ) { Object q=this;
...
...q=that;...
...
foo(q);
}
foo可能会隐藏对X的访问权限。所以你需要两件事:流分析来判断对q的初始赋值是否可以到达调用foo(它可能不会; q =可能主导所有对foo的调用),并且调用图形分析,以确定foo可能实际调用的方法,以便您可以分析那些访问成员X的方法。
如果您没有足够的信息来证明其他情况,您可以决定在多大程度上做出保守的假设“A读取X”。这将给你一个“安全”的答案(如果不是“正确”或我更喜欢称之为“精确”)。
可能有用的框架,您可以考虑使用Mono,它肯定会解析和构建符号表。我不知道它为流分析或调用图提取提供了什么支持;我不希望Mono-to-IL前端编译器做很多事情,因为人们通常会将这些机器隐藏在基于JIT的系统的JIT部分中。缺点是Mono可能落后于“现代C#”曲线;上次我听说,它只处理C#2.0,但我的信息可能是陈旧的。
另一种选择是我们的DMS Software Reengineering Toolkit及其C# Front End。 (不是开源产品)。
DMS提供通用源代码解析,树构建/检查/分析,通用符号表支持和内置机制,用于实现控制流分析,数据流分析,点到分析(需要“对象O点是什么”到?“),并调用图形构造。这个机器都经过了DMS的Java和C前端的测试,并且符号表支持已经用于实现完整的C ++名称和类型解析,所以它非常有效。 (你不想低估构建所有机器所需的工作;自1995年以来我们一直在研究DMS。)
C#前端提供完整的C#4.0解析和完整的树构建。它目前没有为C#构建符号表(我们正在研究这个),与Mono相比这是一个缺点。但是,使用这样的符号表,您可以访问所有流分析机器(已经使用DMS的Java和C前端进行了测试),如果它没有提供,那么可能是Mono的一大步。< / p>
如果你想做得好,你面前有相当多的工作。如果你想坚持“简单”,你只需要解析树,并且非常保守就可以了。
你没有多说过知道一个方法是否写了给一个成员。如果要按照描述的方式最小化流量,则需要区分“读取”,“写入”和“更新”情况并在两个方向上优化消息。对于各种情况,分析显然非常相似。
最后,您可以考虑直接处理MSIL以获取所需的信息;你仍然会有流量分析和保守分析问题。您可能会发现以下技术文章很有趣;它描述了一个完全分布式的Java对象系统,它必须进行你想要做的相同的基本分析, IIRC,通过分析类文件和进行大量字节码重写来实现。 Java Orchestra System
答案 7 :(得分:-2)
我认为你能做的最好就是明确地保持一个脏标志。