dimanche 29 décembre 2019

LINQ - Select property from object array to strongly runtime type array?

Trying to improve a insert method via array binding(Oracle), which will need to put strongly type with Array.

The first step, I need to List<object> to each property Array. However, when I use LINQ to Select the property then ToArray(), I got an object[].

//Reflection
foreach(var prop in typeof(Person).GetProperties())
    personList.Select(x => prop.GetValue(x)).ToArray();

It look like can't use generic ToArray<T>() to handle runtime property type.

Code https://dotnetfiddle.net/7ogYNJ

using System;
using System.Collections.Generic;
using System.Linq;              
using System.Reflection;
using System.Diagnostics;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Program
{
    public static void Main()
    {

        List<Person> personList = new List<Person>();
        personList.Add(new Person(){Name = "aaa", Age = 10});
        personList.Add(new Person(){Name = "bbb", Age = 5});
        for(int i = 0; i< 10; i++)
        {
            personList.AddRange(personList);
        }


        Dictionary<string, PropertyInfo> propDict = typeof(Person).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanRead && !p.GetIndexParameters().Any()).AsEnumerable().ToDictionary(x => x.Name, x => x);
        int count = personList.Count;

        Stopwatch sw = new Stopwatch();
        //1. get Object[]
        sw.Restart();
        foreach(var prop in propDict)
        {
            var tempArray = personList.Select(x => prop.Value.GetValue(x)).ToArray();
            Console.WriteLine(tempArray.GetType().Name);//Object[]
        }
        sw.Stop();
        Console.WriteLine("Spend" + sw.ElapsedMilliseconds + "us");

        //2. not work, return Object[]
        sw.Restart();
        foreach(var prop in propDict)
        {
            var tempArray = personList.Select(x => Convert.ChangeType(prop.Value.GetValue(x), prop.Value.PropertyType)).ToArray();
            Console.WriteLine(tempArray.GetType().Name);//Object[]
        }
        sw.Stop();
        Console.WriteLine("Spend" + sw.ElapsedMilliseconds + "us");

        //3. work, via ArrayCopy, try to find other method
        sw.Restart();
        foreach(var prop in propDict)
        {
            var tempArray = personList.Select(x => prop.Value.GetValue(x)).ToArray();
            Array stronglyTypeArray = Array.CreateInstance(prop.Value.PropertyType, count);
            Array.Copy(tempArray, stronglyTypeArray, count);
            Console.WriteLine(stronglyTypeArray.GetType().Name);//String[]//Int32[]
        }
        sw.Stop();
        Console.WriteLine("Spend" + sw.ElapsedMilliseconds + "us");

        //4. work, if-condition slower than method 3
        sw.Restart();
        foreach(var prop in propDict)
        {
            dynamic tempArray;
            if(prop.Value.PropertyType == typeof(System.String))
            {
                tempArray = personList.Select(x =>  prop.Value.GetValue(x).ToString()).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Boolean))
            {
                tempArray = personList.Select(x =>  (bool)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Byte))
            {
                tempArray = personList.Select(x =>  (byte)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Char))
            {
                tempArray = personList.Select(x =>  (char)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Decimal))
            {
                tempArray = personList.Select(x =>  (decimal)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Double))
            {
                tempArray = personList.Select(x =>  (double)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Int16))
            {
                tempArray = personList.Select(x =>  (short)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Int32))
            {
                tempArray = personList.Select(x =>  (int)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Int64))
            {
                tempArray = personList.Select(x =>  (long)prop.Value.GetValue(x)).ToArray();
            }
            else if(prop.Value.PropertyType == typeof(System.Single))
            {
                tempArray = personList.Select(x =>  (float)prop.Value.GetValue(x)).ToArray();
            }
            else
            {
                tempArray = personList.Select(x =>  prop.Value.GetValue(x)).ToArray();
            }
            Console.WriteLine(tempArray.GetType().Name);//Object[]
        }
        sw.Stop();
        Console.WriteLine("Spend" + sw.ElapsedMilliseconds + "us");
    }
}

In method 2, I try to use Convert.Change and it is not work.
In method 3, via Array.CreateInstance and ArrayCopy, it work.
In method 4, use if-condition to cast, but it is slower than method 3 and can't handle unexpected type.

My candidate is method 3. Is there any more efficient way to get strongly type with Array?

By the way, there is a delegate method to get property value faster https://stackoverflow.com/a/17440469/12620047





Aucun commentaire:

Enregistrer un commentaire