分类: C/C++
2008-08-01 17:02:53
static void Main(string [] args)注意:同样的技巧也可以用到本地托管的DLL或者EXE上。你可以在这些文件上挨个地试一试。如果没有异常被抛出,并且返回了一个有效的名字,那么这个文件就是托管的。当然,这种方式在所有的本地文件都触发异常的情况下也存在一些性能上的缺陷。当然还有另一种方法,它不依赖于反射,也不需要装载Portable Executable (PE)。而是通过分析DLL或者EXE的PE头中某个标识位是否被置位,由此确定它是否是 托管的。Managed Extensions for C requently Asked Questions中有实现这种方法的C 代码,等价的C#代码请参见 Figure 3。
{
if (args.Length > 0)
{
try
{
AssemblyName a = AssemblyName.GetAssemblyName(args[0]);
Console.WriteLine(a.Fullname);
}
catch(Exception exc)
{
Console.WriteLine(exc.Message);
}
}
}
((IDisposable)enumerator).Dispose();如果枚举器没有实现IDisposable接口,那么编译器仍然不得不试着释放这个对象,因为不能确定枚举器的派生类型(从这个函数返回的)没有实现IDisposable接口。于是,编译器又会生成类似这样的一个finally块:
IDisposable disposable = enumerator as IDisposable;
if (disposable != null) disposable.Dispose();
因为大多数的可枚举类都有一个GetEnumerator()方法以返回IEnumerator接口(没有实现IDisposable接口),所以上述的代码只是编译器生成结果的一般情形。在少数情况下,当返回的类型没有实现IDisposable接口而且是密封的(指不能从其再派生新的类)时,编译器就不需要实现一个try/finally块了——因为没有什么需要释放的了。
我希望能枚举出一个对象层中的所有对象,但是我不知道如何才能避免很有可能产生的循环引用。.NET Framework对此提供了什么帮助没有?
遍历一个对象层中的所有对象并以某种形式将它们序列化,非常类似于完成一个BinaryFormatter那样的远程格式化程序。对此,.NET Framework也提供了一些很有用的类来完成这类工作。System.Runtime.Serialization命名空间中的ObjectIDGenerator类可以看作是一张专门化的表,用于跟踪对象并赋与每个对象一个唯一的标识符。你可以通过这个类查询对象的ID——它既可以向你提供继存对象的ID,也可以为一个未出现的对象生成一个新的ID。正因它能防止你两次遍历同一个对象,所以你可以很容易地避免循环引用了。此外,System.Collections命名空间里集合类型的类也能帮你很容易地跟踪需要被检查的对象。
Figure 4
中的代码就是我的具体实现。ObjectWalker类实现了一个IEnumerator接口,这样就可以很容易地通过一个 foreach 循环就把对象层中的所有对象枚举出来了:
foreach(object obj in new ObjectWalker(hierarchy))枚举时先将根对象压栈,每调用一次MoveNext()就出栈一次,再将Current数据属性返回的结果压栈。此外在我的实现里,我还会进一步地利用反射来列出对象内部可能的需要枚举的字段(译注:数据成员)值。如果对象内部有可枚举值,它会把这些枚举值取出来,并且使用ObjectIDGenerator来检测它们是否已经被处理过。反之,这些对象就会被放入处理用的栈以等待后续的操作取用。在检查字段前,这段代码会检查当前对象的类型是否符合终止条件。在这个实现里,当我遇到一些简单类型(比如int或者byte)、字符串string、一个枚举型或者一个指针时,就停止递归。我遇到指针时就停下来,这样可以防止我不慎进入非 托管的内存空间。
{
Console.WriteLine("{0}:\t{1}", obj.GetType(), obj);
}
MyDelegate d = (MyDelegate)Delegate.CreateDelegate(typeof(MyDelegate), theMethodInfo);如果这个方法并不是静态的,那么你可以使用Delegate.CreateDelegate()的另一个重载形式,使用将被调用方法的名字及其实例来产生之前需要的委托对象:
theControl.Invoke(d, new object[]{param1, param2, …});
MyDelegate d = (MyDelegate)Delegate.CreateDelegate(typeof(MyDelegate), targetObj, theMethodInfo.Name);尽管如此,因为仍然需要一个该MethodInfo所对应的委托类型,并且你手里可能也还没有合适的委托类型,因此上述这种方法并不适用于所有的情形。更好的一个办法是建立一个适用于任何MethodInfo的委托类。要这样做,你首先要创建一个委托,它的声明形式对应了MethodInfo.Invoke()方法,第一个参数对应了对目标实例的引用(若是静态方法则是null),第二个参数则是调用所需的一个object类型数组。这个委托的声明可以这么写:
theControl.Invoke(d, new object[]{param1, param2, …});
private delegate object MethodInvokeHandler(object target, object [] parameters);之后你可以创建这个委托的一个实例,包装MethodInfo的Invoke()方法,然后将这个委托实例传递给Control的Invoke()方法,就象这样:
MethodInvokeHandler d = new MethodInvokeHandler(theMethodInfo.Invoke); object [] parameters = new object[]{param1, param2, ...}; theControl.Invoke(d, new object[]{targetObj, parameters});
使用这种方法和之前定义的委托,你可以利用Control.Invoke()方法来调用任何一个MethodInfo对象修饰的方法。
另一种解决办法则是动态地生成一个委托类,这个类适用于形式匹配的方法(参见本月Paul DiLascia与Dino Esposito的专栏中关于代码生成的讨论。译注:即其中利用C#实现类似Spy 的事件查看功能的问答)。然后你就可以使用上文提到的Delegate.CreateDelegate()方法了。为什么你在已有上一种方案后还想动态地生成一个委托?因为先前的方案相对而言太慢了。与直接调用一个方法相比(从某种意义上讲,委托是一个很有效的面向对象的函数指针,尽管它的使用比直接的方法调用费时),该方案让你调用一个委托,通过迟绑定的操作来调用真正的方法。
因为动态生成代码会导致一定程度的运行性能下降,所以你一定要预先就生成这些动态代码,之后你就能在每一次的调用中获得速度上的优势了。如果你要进行一些调用,它需要花费一点启动时间。经我调用目标方法对传入的整数型参数执行一些简单的整数运算所完成的测试表明,使用动态生成委托的方法提高了50%的性能。当然,如果目标方法要真正用于实际应用,那么参数的类型还有商讨的余地。说了那么多关于方法调用的内容,其实与方法内部函数体的执行相比,方法调用的运行时间开销就无足轻重了,所以方法越简单越好。
在你的那些软件工程里,首先要保证代码是清晰的、可运行的。当其性能不能令人满意时,才反复不断地使用性能分析器确定影响性能的关键点并进行修改,以最终达到性能上的要求。
System.Random类使用了什么算法来产生随机数?这个算法是加密的吗?
在目前这个版本的.NET Framework中,System.Random类使用了Donald Knuth描述的一个简化算法(The Art of Computer Programming, Volume 2: Seminumerical Algorithms (Addison-Wesley, 1997))。你可以在SSCLI(Shared Source Common Language Infrastructure,可共享的源通用公共语言规范)中查看System.Random的源码,或者在线查看
random.cs。
System.Random并不是加密的,所以不能用于可能会威胁数据安全的应用中。相对的,在System.Security.Cryptography命名空间中的RNGCryptoServiceProvider类则是一个带加密功能的随机数产生器,可以应用于需要加密随机数的场合。
在上个月的
专栏
文章里,你回答了一个关于如何获取 CLR 安装目录路径的问题。你使用了P/Invoke() 来访问 mscoree.dll 暴露的非托管 GetCORSystemDirectory () 函数。为什么你会选择这种方法,难道没有别的方法了吗?
据我所知,至少还有两种简单的方法获取这个目录路径。第一种方法最简单,调用System.Runtime.InteropServices.RuntimeEnvironment 类的 GetRuntimeDirectory() 方法即可。第二种方法则是先获取 mscorlib.dll 中某种类(比如对象类)的类型,然后使用该类型的 Type 对象来获取 mscorlib 模块的全限定名。在一台安装了 .NET Framework 1.1 的 Windows XP 机器上,mscorlib 的路径可能是这个样子的 %windir%\microsoft.net\framework\v1.1.4322\mscorlib.dll。System.IO.Directory.GetParent() 方法可用以获取其父文件夹的路径。尽管 RuntimeEnvironment.GetRuntimeDirectory() 是最方便的,但它并不是最快的。在我的机器上,调用它来获取该目录路径的时间开销近 5 倍于等价的 interop 方法调用。而使用 Type 对象的方法则更甚,近15倍于 interop 方式的方法调用。当然,这些函数并不需要在一个长时间运行的循环中被调用,所以它们的效率高低并不是什么大问题。我个人认为 interop 是一种很酷的技术,而且 interop 的速度也不赖,所以才想到展示上期提到的那种方法的。
有什么疑问或者意见,请给我发Email:netqa@microsoft.com.