2017-04-24 125 views
1

我可以看到下列在C#中實例化一個int數組的方式有兩種:創建新的數組實例

  1. 通過API在System.Array抽象類:

    var arrayInstance = Array.CreateInstance(typeof(int), 4); 
    
  2. 通過各種數組初始化的語法:

    var arrayInstanceWithSyntax = new int[4]; 
    

以上兩種方式絕對相同嗎?編譯器在編譯時本身(存在於MSIL中)將第二種語法轉換爲第一種語法,還是在運行時發生CLR級別的JIT魔術,或者兩種代碼語法之間根本沒有轉換?

+0

是的,它們依賴於CLR中的相同管道,而不是編譯器參與。 Type.MakeArrayType()重載也更普遍地暴露出來。第二種風味嚴重微觀優化。 –

+0

從早上起,我一直在使用googling來獲取一些內部函數,看看編譯器如何將'new int [4]'轉換爲數組類型實例,因爲'System.Array'是.Net中的抽象類,'int []'不是這些東西可以直接轉換成.Net FCL中的類型。你能幫我一些鏈接,這可以幫助我更多地瞭解這一點。 – RBT

+1

數組類型非常不尋常,CLR動態創建它們。您必須閱讀CLR源代碼才能瞭解它的底部。這可以讓你忙一段時間,需要C++技能,但它可以從CoreCLR github項目中獲得。搜索「AllocateArrayEx」和「FastAllocatePrimitiveArray」以查找最相關的代碼。 –

回答

4

他們絕對創造了同類的價值 - 不同於如果你打電話Array.CreateInstance,並創建一個非零下界的數組,例如。

然而,他們不是在IL方面是相同的 - 首先是一個簡單的方法調用,第二個使用該newarr IL指令。

目前並不需要成爲任何一種「神奇JIT」在這裏 - 只有兩個路徑來創建同一類型的值。

編譯時型的第一個變量就是Array雖然 - 你必須將它轉換爲int[]爲兩段代碼真的有同樣的結果。

我會在可能的情況下始終使用「C#native」數組創建語法 - 僅當由於某種原因使用Type時,纔會使用Array.CreateInstance(而不是在編譯時知道,即使通過泛型類型參數)...或如果你是試圖創建一個可能有一個非零下限的數組。

2

簡短的回答:不,他們產生不同的IL。你可以在Try Roslyn上看到這個。


Array.CreateInstance

CreateInstance方法是在Array類工廠方法,它返回一個類型的Array。這裏是該方法的源代碼:

[System.Security.SecuritySafeCritical] // auto-generated 
public unsafe static Array CreateInstance(Type elementType, int length) 
{ 
    if ((object)elementType == null) 
     throw new ArgumentNullException("elementType"); 
    if (length < 0) 
     throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 
    Contract.Ensures(Contract.Result<Array>() != null); 
    Contract.Ensures(Contract.Result<Array>().Length == length); 
    Contract.Ensures(Contract.Result<Array>().Rank == 1); 
    Contract.EndContractBlock(); 

    RuntimeType t = elementType.UnderlyingSystemType as RuntimeType; 
    if (t == null) 
     throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "elementType"); 
    return InternalCreate((void*)t.TypeHandle.Value, 1, &length, null); 
} 

請注意上述方法中的最後一行代碼。該方法的主體只是一個分號,它是在其他地方實施的一種方法。這裏是身體:

[System.Security.SecurityCritical] // auto-generated 
[ResourceExposure(ResourceScope.None)] 
[MethodImplAttribute(MethodImplOptions.InternalCall)] 
private unsafe static extern Array InternalCreate(void* elementType, int rank, int* pLengths, int* pLowerBounds); 

在哪裏實施?它在arraynative.cpp類中實現。下面是代碼:

FCIMPL4(Object*, ArrayNative::CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pLowerBounds) { 
{ 
    CONTRACTL { 
     FCALL_CHECK; 
     PRECONDITION(rank > 0); 
     PRECONDITION(CheckPointer(pLengths)); 
     PRECONDITION(CheckPointer(pLowerBounds, NULL_OK)); 
    } 
    CONTRACTL_END; 

    OBJECTREF pRet = NULL; 
    TypeHandle elementType = TypeHandle::FromPtr(elementTypeHandle); 

    _ASSERTE(!elementType.IsNull()); 

    // pLengths and pLowerBounds are pinned buffers. No need to protect them. 
    HELPER_METHOD_FRAME_BEGIN_RET_0(); 

    CheckElementType(elementType); 

    CorElementType CorType = elementType.GetSignatureCorElementType(); 

    CorElementType kind = ELEMENT_TYPE_ARRAY; 

    // Is it ELEMENT_TYPE_SZARRAY array? 
    if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0) 
# ifdef FEATURE_64BIT_ALIGNMENT 
     // On platforms where 64-bit types require 64-bit alignment and don't obtain it naturally force us 
     // through the slow path where this will be handled. 
     && (CorType != ELEMENT_TYPE_I8) 
     && (CorType != ELEMENT_TYPE_U8) 
     && (CorType != ELEMENT_TYPE_R8) 
#endif 
    ) 
    { 
     // Shortcut for common cases 
     if (CorTypeInfo::IsPrimitiveType(CorType)) 
     { 
      pRet = AllocatePrimitiveArray(CorType, pLengths[0]); 
      goto Done; 
     } 
     else 
     if (CorTypeInfo::IsObjRef(CorType)) 
     { 
      pRet = AllocateObjectArray(pLengths[0], elementType); 
      goto Done; 
     } 

     kind = ELEMENT_TYPE_SZARRAY; 
     pLowerBounds = NULL; 
    } 

    { 
     // Find the Array class... 
     TypeHandle typeHnd = ClassLoader::LoadArrayTypeThrowing(elementType, kind, rank); 

     DWORD boundsSize = 0; 
     INT32* bounds; 
     if (pLowerBounds != NULL) 
     { 
      if (!ClrSafeInt <DWORD>::multiply(rank, 2, boundsSize)) 
       COMPlusThrowOM(); 
      DWORD dwAllocaSize = 0; 
      if (!ClrSafeInt <DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize)) 
       COMPlusThrowOM(); 

      bounds = (INT32*)_alloca(dwAllocaSize); 

      for (int i = 0; i < rank; i++) 
      { 
       bounds[2 * i] = pLowerBounds[i]; 
       bounds[2 * i + 1] = pLengths[i]; 
      } 
     } 
     else 
     { 
      boundsSize = rank; 

      DWORD dwAllocaSize = 0; 
      if (!ClrSafeInt <DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize)) 
       COMPlusThrowOM(); 

      bounds = (INT32*)_alloca(dwAllocaSize); 

      // We need to create a private copy of pLengths to avoid holes caused 
      // by caller mutating the array 
      for (int i = 0; i < rank; i++) 
       bounds[i] = pLengths[i]; 
     } 

     pRet = AllocateArrayEx(typeHnd, bounds, boundsSize); 
    } 

    Done:; 
    HELPER_METHOD_FRAME_END(); 

    return OBJECTREFToObject(pRet); 
} 

正如你可以看到Array.CreateInstance使用其它地方執行外部DLL,託管代碼之外。


新INT [4];

這是C#原生的,所以C#編譯器會照顧它並創建數組。怎麼樣?我不確定。

我希望澄清一點事情。