0

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

  • Why not using `var nameArray = personList.Select(t=>t.Name).ToArray()` for each property? You want to create a generic method that handles it? – Eldar Dec 29 '19 at 20:05
  • @Eldar, I want to create a generic method public void someMethod(List InputList). So, I can't select property directly. Instead, I get the property array by Reflection typeof(Person).GetProperty("Name").GetValue(x) – littletree71 Dec 30 '19 at 01:44
  • Do you know the type returned by `GetValue()`? Does that explain why the type returned by `ToArray()` is `object[]`? Similarly, what is the return type of `Convert.ChangeType()`? Knowing the (compile time) type is half the battle. – NetMage Dec 30 '19 at 23:28

1 Answers1

2

How about switching on type (generally frowned upon) and using Cast<>() to create the correct Array:

foreach (var prop in propDict) {
    var qryProp = personList.Select(x => prop.Value.GetValue(x));
    Array tempArray = null;
    switch (Type.GetTypeCode(prop.Value.PropertyType)) {
        case TypeCode.String:
            tempArray = qryProp.Cast<string>().ToArray();
        break;
        case TypeCode.Int32:
            tempArray = qryProp.Cast<Int32>().ToArray();
        break;
    }
    Console.WriteLine(tempArray.GetType().Name);//String[]/Int32[]
}
NetMage
  • 26,163
  • 3
  • 34
  • 55