Почему я не могу использовать DateTime [] для объекта []?

Кажется, что я могу использовать DateTime для объекта, поэтому почему я не могу использовать массив DateTime [] для объекта []? Я знаю, что это имеет какое-то отношение к значениям/ссылочным типам, но разве бокс не позволяет мне это делать?

Ответ 1

Ковариация массивов применяется только к массивам ссылочных типов. DateTime - это тип значения, поэтому вы не можете назначить DateTime[] переменной object[]. Вам нужно будет явно создать массив объектов и скопировать значения. Другими словами, создайте новый экземпляр массива типа object[].

Есть много способов сделать это. Простое использование CopyTo() должно быть достаточно.

DateTime[] x = new DateTime[] { ... };
object[] y = new object[x.Length];
x.CopyTo(y, 0);

Я провел несколько тестов. Вероятно, это не лучший способ сделать это, но он должен дать хорошее представление о том, что будет с правильным профилировщиком.

class Program
{
    static void Main(string[] args)
    {
        var now = DateTime.Now;
        var dates = new DateTime[5000000];
        for (int i = 0; i < dates.Length; i++)
            dates[i] = now.AddSeconds(i);
        for (int i = 0; i < 5; i++)
        {
            Test("Test1", () =>
            {
                var result = new object[dates.LongLength];
                for (long l = 0; l < result.LongLength; l++)
                    result[l] = dates[l];
                return result;
            });
            Test("Test2", () =>
            {
                var result = new object[dates.LongLength];
                dates.CopyTo(result, 0);
                return result;
            });
            Test("Test3", () =>
            {
                var result = new object[dates.LongLength];
                Array.Copy(dates, result, dates.LongLength);
                return result;
            });
            Test("Test4", () =>
            {
                var result = Array.ConvertAll(dates, d => (object)d);
                return result;
            });
            Test("Test5", () =>
            {
                var result = dates.Cast<object>().ToArray();
                return result;
            });
            Test("Test6", () =>
            {
                var result = dates.Select(d => (object)d).ToArray();
                return result;
            });
            Console.WriteLine();
        }
    }

    static void Test<T>(string name, Func<T> fn)
    {
        var startMem = GC.GetTotalMemory(true);
        var sw = Stopwatch.StartNew();
        var result = fn();
        sw.Stop();
        var endMem = GC.GetTotalMemory(false);
        var diff = endMem - startMem;
        Console.WriteLine("{0}\tMem: {1,7}/{2,7} ({3,7})", name, startMem, endMem, diff);
        Console.WriteLine("\tTime: {0,7} ({1,7})", sw.ElapsedMilliseconds, sw.ElapsedTicks);
    }
}

Технические характеристики:
Win7Pro x64, Core2Quad [email protected], 4GiB DDR2 1066 (PC2-8500)
64-битная сборка (32-разрядная версия примерно одинаковая, всего меньше памяти)

Test1   Mem: 40086256/200087360 (160001104)
        Time:     444 (1230723)
Test2   Mem: 40091352/200099272 (160007920)
        Time:     751 (2078001)
Test3   Mem: 40091416/200099256 (160007840)
        Time:     800 (2213764)
Test4   Mem: 40091480/200099256 (160007776)
        Time:     490 (1358326)
Test5   Mem: 40091608/300762328 (260670720)
        Time:    1407 (3893922)
Test6   Mem: 40091672/300762328 (260670656)
        Time:     756 (2092566)

Test1   Mem: 40091736/200099184 (160007448)
        Time:     515 (1425098)
Test2   Mem: 40091736/200099184 (160007448)
        Time:     868 (2404151)
Test3   Mem: 40091736/200099160 (160007424)
        Time:     885 (2448850)
Test4   Mem: 40091736/200099184 (160007448)
        Time:     540 (1494429)
Test5   Mem: 40091736/300762240 (260670504)
        Time:    1479 (4093676)
Test6   Mem: 40091736/300762216 (260670480)
        Time:     746 (2065095)

Test1   Mem: 40091736/200099168 (160007432)
        Time:     500 (1383656)
Test2   Mem: 40091736/200099160 (160007424)
        Time:     781 (2162711)
Test3   Mem: 40091736/200099176 (160007440)
        Time:     793 (2194605)
Test4   Mem: 40091736/200099184 (160007448)
        Time:     486 (1346549)
Test5   Mem: 40091736/300762232 (260670496)
        Time:    1448 (4008145)
Test6   Mem: 40091736/300762232 (260670496)
        Time:     749 (2075019)

Test1   Mem: 40091736/200099184 (160007448)
        Time:     487 (1349320)
Test2   Mem: 40091736/200099176 (160007440)
        Time:     781 (2162729)
Test3   Mem: 40091736/200099184 (160007448)
        Time:     800 (2214766)
Test4   Mem: 40091736/200099184 (160007448)
        Time:     506 (1400698)
Test5   Mem: 40091736/300762224 (260670488)
        Time:    1436 (3975880)
Test6   Mem: 40091736/300762232 (260670496)
        Time:     743 (2058002)

Test1   Mem: 40091736/200099184 (160007448)
        Time:     482 (1335709)
Test2   Mem: 40091736/200099184 (160007448)
        Time:     777 (2150719)
Test3   Mem: 40091736/200099184 (160007448)
        Time:     793 (2196184)
Test4   Mem: 40091736/200099184 (160007448)
        Time:     493 (1365222)
Test5   Mem: 40091736/300762240 (260670504)
        Time:    1434 (3969530)
Test6   Mem: 40091736/300762232 (260670496)
        Time:     746 (2064278)

Интересно, что ConvertAll() выполняет те же действия, что и обычный цикл.

Ответ 2

Вы не можете наложить DateTime[] на object[], потому что это будет небезопасно. Все массивы опорных типов одинаковой длины имеют одинаковый макет в памяти. DateTime - тип значения, а массив - "плоский" (unboxed). Вы не можете безопасно использовать object[], потому что макет в памяти несовместим с object[].

Ответ 3

Если у вас есть LINQ (.NET 3.5+), вы можете сделать:

DateTime[] dates = new DateTime[3];

dates[0] = new DateTime(2009, 01, 01);
dates[1] = new DateTime(2010, 01, 01);
dates[2] = new DateTime(2011, 01, 01);

object[] dates2 = Array.ConvertAll(dates, d => (object)d);

Как отметил Джефф, вы также можете сделать аналогичную вещь в .NET 2.0 с помощью делегатов:

object[] dates3 = Array.ConvertAll(dates, 
                        delegate(DateTime d) { return (object)d; });

Ответ 4

Кстати, вы можете выполнить это, используя Array.Copy()

void Main()
{
    DateTime[] dates = new DateTime[] { new DateTime(2000, 1, 1), new DateTime (2000, 3, 25) };
    object[] objDates = new object[2];
    Array.Copy(dates, objDates, 2);

    foreach (object o in objDates) {
        Console.WriteLine(o);
    }
}

Ответ 5

Потому что DateTime - a object, но массив DateTime не - массив object.

Массивы типов значений отличаются от массивов ссылочных типов, поэтому эти два типа массивов принципиально несовместимы. Массив типа значения фактически содержит значения, и массив ссылочного типа содержит, ну, только ссылки.

Ответ 6

См. другие ответы, почему вы не можете этого сделать.

Альтернативой является выполнение глубокой копии массива. Пример использования LINQ:

DateTime[] dates = ...;
object[] objects = dates.Select(d => (object)d).ToArray();