Array.CreateInstance与使用new运算符创建新数​​组实例之间的区别

时间:2017-04-24 06:15:03

标签: c# .net arrays initialization

我可以看到以下两种在C#中实例化int数组的方法:

  1. 通过System.Array抽象类中的API:

    var arrayInstance = Array.CreateInstance(typeof(int), 4);
    
  2. 通过各种数组初始化语法:

    var arrayInstanceWithSyntax = new int[4];
    
  3. 上述两种方式完全相同吗?编译器是否在编译时将第二种语法转换为第一种语法(存在于MSIL中),或者在CLR级别存在一些JIT魔术,它在运行时发生,或者两种代码语法之间根本没有转换?

2 个答案:

答案 0 :(得分:5)

它们肯定会创建相同类型的值 - 例如,与调用Array.CreateInstance并创建一个非零下限的数组不同。

然而,它们在IL方面并不相同 - 第一个只是方法调用,第二个使用newarr IL指令。

不需要任何类型的" JIT魔法"这里 - 只有两条路径可以创建相同类型的值。

您的第一个变量的编译时类型只是Array - 您必须将其转换为int[],以便将两个代码转换为Array.CreateInstance真的有相同的结果。

我会一直使用" C#native"尽可能使用数组创建语法 - 当出于某种原因(而不是在编译时知道,甚至通过泛型类型参数)时,仅使用Type ...或者如果你'重新尝试创建一个可能具有非零下限的数组。

答案 1 :(得分:2)

简答:不会产生不同的IL。您可以在Try Roslyn上自行查看。

<强> Array.CreateInstance

I/System.out: client I/BluetoothSocket.cpp: Setting Master socket option I/System.out: connecting W/System.err: java.io.IOException: Unable to start Service Discovery W/System.err: at android.bluetooth.BluetoothSocket$SdpHelper.doSdp(BluetoothSocket.java:494) W/System.err: at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:252) W/System.err: at ml.minary.example.MainActivity.onConnectAsClientClick(MainActivity.java:61) W/System.err: at java.lang.reflect.Method.invokeNative(Native Method) W/System.err: at java.lang.reflect.Method.invoke(Method.java:511) W/System.err: at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) W/System.err: at android.view.View.performClick(View.java:4336) W/System.err: at android.view.View$PerformClick.run(View.java:17587) W/System.err: at android.os.Handler.handleCallback(Handler.java:725) W/System.err: at android.os.Handler.dispatchMessage(Handler.java:92) W/System.err: at android.os.Looper.loop(Looper.java:137) W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5230) W/System.err: at java.lang.reflect.Method.invokeNative(Native Method) W/System.err: at java.lang.reflect.Method.invoke(Method.java:511) W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797) W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:564) W/System.err: at dalvik.system.NativeStart.main(Native Method) 方法是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);
}

在哪里实施?它在 arraynative.cpp 类中实现。这是代码:

[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);

正如您所看到的,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); } 使用在托管代码之外的其他地方实现的外部DLL。

new int [4];

这是C#的原生代码,因此C#编译器会处理它并创建数组。怎么样?我不确定。

我希望能澄清一些事情。