Я вижу следующие два способа создания массива int в C #:

  1. Через API в System.Array абстрактном классе:

    var arrayInstance = Array.CreateInstance(typeof(int), 4);
    
  2. Через различные синтаксис инициализации массива:

    var arrayInstanceWithSyntax = new int[4];
    

Вышеупомянутые два способа абсолютно идентичны? Преобразует ли компилятор второй синтаксис в первый синтаксис во время самой компиляции (присутствует в MSIL), или существует некоторое волшебство JIT на уровне CLR, которое происходит во время выполнения, или нет никакого преобразования между двумя синтаксисами кода?

4
RBT 24 Апр 2017 в 09:15

2 ответа

Лучший ответ

Они определенно создают одно и то же значение - в отличие от того, что вы вызываете, например, Array.CreateInstance и создаете массив с ненулевой нижней границей.

Тем не менее, они не одинаковы с точки зрения IL - первый - просто вызов метода, второй использует инструкцию newarr IL.

Здесь не должно быть никакой «магии JIT» - есть только два пути для создания одинаковых значений.

Тип вашей первой переменной время компиляции просто Array - вам нужно привести его к int[], чтобы два куска кода действительно имели одинаковый результат ,

Я бы всегда использовал синтаксис создания массива "C # native" там, где это возможно - используйте Array.CreateInstance только тогда, когда у вас есть элемент Type по какой-то причине (вместо того, чтобы знать во время компиляции, даже через параметр универсального типа) ... или если вы пытаетесь создать массив, который может иметь ненулевую нижнюю границу.

8
Jon Skeet 24 Апр 2017 в 06:18

Краткий ответ: Нет, они производят разные IL. Вы можете увидеть это сами по Попробуйте 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, реализованную в другом месте, за пределами управляемого кода.


new int [4];

Это встроено в C #, поэтому компилятор C # позаботится об этом и создаст массив. Как? Я не уверена.

Надеюсь, это немного прояснит ситуацию.

3
CodingYoshi 24 Апр 2017 в 17:03
43580956