简体   繁体   中英

Why can't I cast DateTime[] to object[]?

It seems that I can cast DateTime to object, so why can't I cast array DateTime[] to object[]? I know this has something to do with value/reference types, but doesn't boxing allow me to do this?

Array covariance only applies to arrays of reference types. DateTime is a value type so you can't assign a DateTime[] to an object[] variable. You'll have to explicitly create an object array and copy the values over. In other words, create a new array instance of type object[] .

There are plenty of ways you can do this. A simple use of CopyTo() should be enough.

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

I ran some tests. Probably not the best way to do it but it should give a good idea of what it would be with a proper profiler.

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

Specs:
Win7Pro x64, Core2Quad Q9550@2.83GHz, 4GiB DDR2 1066 (PC2-8500)
64-bit build (32-bit is roughly the same, just less memory overall)

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)

Interestingly, ConvertAll() performs much the same as a plain loop.

You can't cast DateTime[] to object[] because it would be unsafe. All arrays of reference types of the same length have the same layout in memory. DateTime is value type, and the array is "flat" (unboxed). You can't safely cast to object[] because the layout in memory is incompatible with object[] .

If you've got LINQ available (.NET 3.5+) you can do:

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

As Jeff pointed out you can also do a similar thing in .NET 2.0 using delegates:

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

BTW, you can accomplish this using 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);
    }
}

Because DateTime is an object , but an array of DateTime isn't an array of object .

Arrays of value types are different than arrays of reference types so these two types of arrays are fundamentally incompatible. The value type array actually contains the values and the reference type array contains, well, only references.

See the other answers for why you cannot do so.

The alternative is to perform a deep copy of the array. Example using LINQ:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM