可以检查核心C#功能的内部

时间:2012-06-02 16:24:35

标签: c# implementation il mscorlib

我今天感到很震惊,我倾向于比较Buffer.BlockCopyArray.CopyTo.的内部结构。我很想知道Array.CopyTo是否在幕后调用了Buffer.BlockCopy。这背后没有实际用途,我只想进一步了解C#语言及其实现方式。不要跳枪并指责我微观优化,但你可以指责我好奇!

当我在mscorlib.dll上运行ILdasm时,我收到Array.CopyTo

.method public hidebysig newslot virtual final 
    instance void  CopyTo(class System.Array 'array',
                          int32 index) cil managed
{
  // Code size       0 (0x0)
} // end of method Array::CopyTo

Buffer.BlockCopy

.method public hidebysig static void  BlockCopy(class System.Array src,
                                            int32 srcOffset,
                                            class System.Array dst,
                                            int32 dstOffset,
                                            int32 count) cil managed internalcall
{
  .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 ) 
} // end of method Buffer::BlockCopy
坦率地说,这令我感到困惑。我从未在我没有创建的dll / exe上运行ILdasm。这是否意味着我将无法看到这些功能是如何实现的?搜索周围只显示a stackoverflow question,Marc Gravell说

  

[Buffer.BlockCopy]基本上是原始mem-copy

的包装器

虽然很有见地,但如果Array.CopyTo调用Buffer.BlockCopy,则无法解答我的问题。我特别感兴趣的是,如果我能够看到这两个函数是如何实现的,如果我将来有关于C#内部的问题,我是否有可能对其进行调查。或者我运气不好?

5 个答案:

答案 0 :(得分:9)

这里最大的问题是你正在查看引用程序集,特别是对于那些.NET 4,从你发布的内容来看。哪些是特殊程序集,它们的所有IL都被剥离出来并且只包含元数据。这是.NET 4中的新功能,它解决了早期版本.NET中的一个老问题。参考组件只是GAC中安装的实际组件的副本。

这导致了麻烦,在以后的版本和破坏程序的服务包中进行了更改。特别是WaitHandle.WaitOne(int)重载是臭名昭着的,它是在.NET 3.0(又名.NET 2.0 SP1)中添加的。并且不知不觉地被程序员使用,发现重载更容易使用heckofalot然后神秘的WaitOne(int,bool)重载。但问题是他们的程序将不再在原始的.NET 2.0发行版本上运行,从而产生MissingMethodException。

添加这个重载通常是一个非常顽皮的事情,他们修改了mscorlib.dll,但更改了它的[AssemblyVersion]。通过在.NET 4中提供单独的引用程序集,不再出现此问题。 Microsoft现在可以修改.NET类型的公共接口而不会破坏任何内容。并且已经热情地完成了这项工作,一些.NET 4中间版本已经流畅播放,没有人注意到。

因此,请务必反汇编mscorlib.dll的真正的版本,即GAC中的版本。其中.NET 4存储在不同的目录中,c:\ windows \ microsoft.net \ assembly,而不是c:\ windows \ assembly。它不再受explorer shell命名空间扩展的保护,您只需使用File + Open浏览GAC目录即可。您将在C:\ Windows \ Microsoft.NET \ assembly \ GAC_32 \ mscorlib \ v4.0_4.0.0.0__b77a5c561934e089目录中找到32位版本。

这不是故事的结尾,当您向下钻取时,您会发现Array.CopyTo()调用一个名为Array.Copy()的内部帮助器方法,该方法具有[MethodImpl(MethodImplOptions.InternalCall)]属性。再一次,没有方法体。该属性告诉即时编译器该方法实际上是在CLR内部的C ++中实现的。请参阅this answer以了解如何查看此类方法的源代码。

答案 1 :(得分:4)

ILSpy是一个免费的.NET反编译器。您可以使用它来检查任何.NET DLL,包括mscorlib。

Array.CopyTo来电Array.CopyArray.CopyBuffer.BlockCopy都是extern方法,这意味着它们是在本机代码中定义的,而不是托管的.NET代码,所以我不能告诉你它们是如何工作的。< / p>

答案 2 :(得分:1)

我将回答我自己的问题。我意识到这是不好的风格,但如果不是因为以前的答案提供给我的精彩资源,我就无法制定答案。谢谢。

首先,那些来到这里并想知道如何一般地检查C#函数内部的人,Tim发布了一个很棒的资源,ILSpy。这适用于未在外部定义方法的情况。当它们在外部定义时,似乎唯一希望获得答案的是您下载SSCLI 2.0。由于这是针对.Net 2.0而非4.0,因此我提供的信息可能已过时。但是,我将继续假设所讨论的方法没有太大变化。浏览完源文件后,我相信我可以回答“在幕后调用Buffer.BlockCopy吗?Array.CopyTopy吗?”

在我了解问题的核心之前,其他人已经指出CopyTo调用Array.Copy。 Array.Copy是在外部定义的,所以我将查询更改为“在幕后调用Array.Copy调用Buffer.BlockCopy吗?”我发现有趣的一点点来自documentation of Array.CopyTo on MSDN

  

如果未明确要求实现System.Collections.ICollection,请使用[Array。] Copy以避免额外的间接。

让我区分每个要执行的功能必须为真的检查类型:

BlockCopy:

  1. 目的地和来源不能为空
  2. Destination和Source数组由基元或字符串组成 但没有对象。
  3. 长度和偏移量必须有效
  4. Array.Copy:

    1. 目的地和来源不能为空
    2. 目标和来源必须是数组
    3. 使用方法表和排名进行的几项检查
    4. 稍微深入一些长度和偏移检查
    5. 类型必须以某种方式匹配(使用un / boxing,cast或者 加宽)
    6. 在这些检查之后,BlockCopy调用m_memmove,这是不言自明的,但非常有趣。 m_memmove依赖于架构;在32位机器上放弃传统的memmove,支持一次手动滚动16个字节。

      可以想象,阵列不仅仅是基元。

      • 如果源阵列和目标阵列属于同一类型,则复制呼叫 m_memmove。然后,如果底层类型不是原始类型,则a 垃圾收集行动已经制定。
      • 否则,根据传入的元素类型,复制将取消装箱, 在传输它们时,对每个元素进行框,转换或加宽。这些 据我所知,函数不调用m_memmove,而是一次转换一个元素。

      因此,回答我原来的问题,“Array.Copy会在幕后调用Buffer.BlockCopy吗?”排序,如果我们考虑两种方法在m_memmove中的相似之处。 BlockCopy将始终调用m_memmove,而Copy只会在处理完全相同类型的数组时调用它。然后我的建议是,如果想要将一个字节数组复制到整数或类似于他们只关心原始数据的东西,使用BlockCopy,因为它可以利用m_memmove。

      供参考:.Net定义为基元(取自cortypeinfo.h)

      1. 空隙
      2. 布尔
      3. 字符
      4. 签名和无符号字节
      5. 已签名且未签名的简短
      6. 签名和无符号int
      7. 签名和签名长
      8. 浮动并加倍
      9. 已签名和未签名的IntPtr
      10. ELEMENT_TYPE_R(不确定这是什么)

答案 3 :(得分:0)

当我在Buffer.BlockCopy上使用Telerik JustDecompile时,我发现:

[SecuritySafeCritical]
public static extern void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count);

这意味着它没有使用产生IL代码的东西实现。

和Array.CopyTo:

public void CopyTo(Array array, int index) {
  if (array != null && array.Rank != 1) {
    throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
  }
  Array.Copy(this, this.GetLowerBound(0), array, index, this.Length);
}

Array.Copy显示:

[SecuritySafeCritical]
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
public static void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) {
  Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, false);
}

那个过载:

[SecurityCritical]
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);

所以这也不是IL代码。

如果你想检查方法,你必须带出反汇编程序。

答案 4 :(得分:0)

Array.CopyToBuffer.BlockCopy都是(最终)使用伪属性实现的:

[MethodImpl(MethodImplOptions.InternalCall)]

如果你看看普通聪明人史蒂文·图布的this page上的第二个问题,那就是对这意味着什么有很好的解释。