简体   繁体   English

C#如何在不进行手动类型转换的情况下调用FieldInfo.SetValue函数

[英]C# How to call FieldInfo.SetValue function without manual type conversion

I want to save data-class instance to database and load it from database. 我想将数据类实例保存到数据库并从数据库加载它。 and I want to generate sql command automatically. 我想自动生成sql命令。 so, I think that I need to use dictionary< string, string> so that solve it. 所以,我认为我需要使用dictionary <string,string>来解决它。

refer my old question : How to convert 2D string array to 2D [int, double, bool, ..] array? 请参阅我的老问题: 如何将2D字符串数组转换为2D [int,double,bool,..]数组?

working process like these. 这样的工作过程。

  1. convert data-class instance to dictionary < string, string>. 将数据类实例转换为字典<string,string>。
  2. save/load dictionary to/from txt, database, etc. 将字典保存到txt,数据库等/从中保存字典。
  3. convert dictionary to data-class instance 将字典转换为数据类实例

I think I have solved the problem anyway. 我想我已经解决了这个问题。 but I think converting 2D array method is still not a perfect one. 但是我认为转换2D数组方法仍然不是一个完美的方法。 I wonder, when I call FieldInfo.SetValue function, is there another way to solve type conversion without using switch/case state like my solution. 我想知道,当我调用FieldInfo.SetValue函数时,是否有另一种方法可以像不使用解决方案那样使用开关/案例状态来解决类型转换。

help me simplify my code. 帮助我简化代码。

Data class like this 这样的数据类

        public class DCylinderData
    {
        public int ID;

        public int[] Solenoid = new int[2];
        public int[] UpSensor = new int[DEF_MAX_CYLINDER_SENSOR];
        public int[] DownSensor = new int[DEF_MAX_CYLINDER_SENSOR];

        public double MovingTime;

        public ECylinderType CylinderType;
        public ESolenoidType SolenoidType;

        public bool[] boolTest = new bool[3];
        public string[] nameTest = new string[2];
        public int[,] TwoDimension = new int[3,4];

        public DCylinderData()
        {
        }
    }

main code like this 像这样的主要代码

            // 0. initialize
        DCylinderData cylData = new DCylinderData();
        cylData.ID = 99;
        cylData.MovingTime = 1.1;
        cylData.CylinderType = ECylinderType.UPSTREAM_DOWNSTREAM;
        cylData.Solenoid = new int[]{ 2, 3};
        for (int i = 0; i < 2 ; i++)
        {
            cylData.Solenoid[i] = i + 2;
            cylData.nameTest[i] = $"NameTest_{i}";
        }
        for (int i = 0; i < DEF_MAX_CYLINDER_SENSOR; i++)
        {
            cylData.UpSensor[i] = i * 1;
            cylData.DownSensor[i] = i * 4;
        }
        for (int i = 0; i < cylData.TwoDimension.GetLength(0) ; i++)
        {
            for (int j = 0; j < cylData.TwoDimension.GetLength(1) ; j++)
            {
                cylData.TwoDimension[i, j] = i * cylData.TwoDimension.GetLength(1) + j;
            }
        }
        cylData.boolTest[0] = true;
        cylData.boolTest[1] = false;
        cylData.boolTest[2] = true;

        // 1. Class -> Dictionary
        Dictionary<string, string> fieldBook = new Dictionary<string, string>();

        Type type = typeof(DCylinderData);
        FieldInfo[] fields = type.GetFields();
        foreach (FieldInfo field in fields)
        {
            // 1.1 element
            if (field.FieldType.IsValueType)
            {
                fieldBook.Add(field.Name, field.GetValue(cylData).ToString());
            }
            // 1.2 array
            else if (field.FieldType.IsArray)
            {
                Array array = (Array)field.GetValue(cylData);

                // 1.2.1 1-D array
                if (array.Rank == 1)
                {
                    for (int i = 0; i < array.GetLength(0); i++)
                    {
                        fieldBook.Add($"{field.Name}__{i}", array.GetValue(i).ToString());
                    }
                }
                // 1.2.2 2-D array
                else if (array.Rank == 2)
                {
                    for (int i = 0; i < array.GetLength(0); i++)
                    {
                        for (int j = 0; j < array.GetLength(1); j++)
                        {
                            fieldBook.Add($"{field.Name}__{i}__{j}", array.GetValue(i, j).ToString());
                        }
                    }
                }
                else
                {
                    WriteLine($"Not support {field.Name}'s array {array.Rank} dimension.");
                }
            }
            else
            {
                WriteLine($"Not support to handle {field.Name}'s {field.FieldType.ToString()}");
            }
        }

        // 2. print Dictionary
        foreach (KeyValuePair<string, string> item in fieldBook)
        {
            WriteLine($"FieldBook {item.Key} : {item.Value}");
        }

        // 3. Dictionary -> Class
        DCylinderData copyData = new DCylinderData();
        foreach (FieldInfo field in fields)
        {
            // 3.1 handle element
            if (field.FieldType.IsValueType && fieldBook.ContainsKey(field.Name))
            {
                SetFieldValue(copyData, field, fieldBook[field.Name]);
            }
            // 3.2 handle array
            else if (field.FieldType.IsArray)
            {
                Array array = (Array)field.GetValue(copyData);
                string key, value;

                // 3.2.1 1-D array
                if (array.Rank == 1)
                {
                    var arr_1d = new string[array.GetLength(0)];
                    for (int i = 0; i < array.GetLength(0); i++)
                    {
                        key = $"{field.Name}__{i}";
                        value = fieldBook.ContainsKey(key) ? fieldBook[key] : "";
                        arr_1d.SetValue(value, i);
                    }
                    SetFieldValue(copyData, field, arr_1d);
                }
                // 3.2.1 2-D array
                else if (array.Rank == 2)
                {
                    var arr_2d = new string[array.GetLength(0), array.GetLength(1)];
                    for (int i = 0; i < array.GetLength(0); i++)
                    {
                        for (int j = 0; j < array.GetLength(1); j++)
                        {
                            key = $"{field.Name}__{i}__{j}";
                            value = fieldBook.ContainsKey(key) ? fieldBook[key] : "";
                            arr_2d.SetValue(value, i, j);
                        }
                    }
                    SetFieldValue(copyData, field, arr_2d);
                }
                else
                {
                    WriteLine($"Not support {field.Name}'s array {array.Rank} dimension.");
                }
            }
            // 3.3 not support
            else
            {
                WriteLine($"Not support to handle {field.Name}'s {field.FieldType.ToString()}");
            }
        }

        WriteLine("Press any key to continue");
        ReadLine();

and, SetFieldValue Functions like these 并且,像这样的SetFieldValue函数

        public static void SetFieldValue(Object target, FieldInfo fieldInfo, string value)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();

        switch (fieldType)
        {
            case "boolean":
                bool b;
                fieldInfo.SetValue(target, bool.TryParse(value, out b) ? b : false);
                break;

            case "int32":
                int n;
                fieldInfo.SetValue(target, int.TryParse(value, out n) ? n : 0);
                break;

            case "double":
                double d;
                fieldInfo.SetValue(target, double.TryParse(value, out d) ? d : 0);
                break;

            case "string":
                fieldInfo.SetValue(target, value);
                break;
        }
    }

    public static void SetFieldValue(Object target, FieldInfo fieldInfo, string[] arr)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();
        fieldType = fieldType.Replace("[]", "");

        switch (fieldType)
        {
            case "boolean":
                bool b;
                bool[] arr_b = Array.ConvertAll(arr, s => bool.TryParse(s, out b) ? b : false);
                fieldInfo.SetValue(target, arr_b);
                break;

            case "int32":
                int n;
                int[] arr_n = Array.ConvertAll(arr, s => int.TryParse(s, out n) ? n : 0);
                //int[] arr_n1 = Array.ConvertAll(arr, int.Parse);
                //int[] arr_n2 = arr.Select(s => int.TryParse(s, out n) ? n : 0).ToArray();
                fieldInfo.SetValue(target, arr_n);
                break;

            case "double":
                double d;
                double[] arr_d = Array.ConvertAll(arr, s => double.TryParse(s, out d) ? d : 0);
                fieldInfo.SetValue(target, arr_d);
                break;

            case "string":
                fieldInfo.SetValue(target, arr);
                break;
        }
    }

    public static void SetFieldValue(Object target, FieldInfo fieldInfo, string[,] arr)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();
        fieldType = fieldType.Replace("[,]", "");

        // 0. string return
        switch (fieldType)
        {
            case "string":
                fieldInfo.SetValue(target, arr);
                return;
                break;
        }

        // 1. initialize
        int n;
        double d;
        bool b;

        //object[,] output = new object[arr.GetLength(0), arr.GetLength(1)];
        int[,] output_n = new int[arr.GetLength(0), arr.GetLength(1)];
        bool[,] output_b = new bool[arr.GetLength(0), arr.GetLength(1)];
        double[,] output_d = new double[arr.GetLength(0), arr.GetLength(1)];

        // 2. convert
        for (int i = 0; i < arr.GetLength(0); i++)
        {
            for (int j = 0; j < arr.GetLength(1); j++)
            {
                switch (fieldType)
                {
                    case "boolean":
                        output_b[i, j] = bool.TryParse(arr[i, j], out b) ? b : false;
                        break;

                    case "int32":
                        output_n[i, j] = int.TryParse(arr[i, j], out n) ? n : 0;
                        break;

                    case "double":
                        output_d[i, j] = double.TryParse(arr[i, j], out d) ? d : 0;
                        break;
                }
            }
        }

        // 2. setvalue
        //fieldInfo.SetValue(target, output);
        switch (fieldType)
        {
            case "boolean":
                fieldInfo.SetValue(target, output_b);
                break;

            case "int32":
                fieldInfo.SetValue(target, output_n);
                break;

            case "double":
                fieldInfo.SetValue(target, output_d);
                break;
        }
    }

this is all part of my code. 这都是我的代码的一部分。

You will need GetElementType , Array.CreateInstance , TypeDescriptor.GetConverter and dynamic (optional). 您将需要GetElementTypeArray.CreateInstanceTypeDescriptor.GetConverterdynamic (可选)。 You will still need quite a bit of code to do it since you need to cater for exceptions and unknown types, and errors in the string. 您仍然需要大量代码来执行此操作,因为您需要迎合异常和未知类型以及字符串中的错误。

To answer you question, you can do 3 things: 要回答您的问题,您可以做三件事:

Replace the 3 lines starting with string fieldType = fieldInfo.FieldType.Name; 替换以string fieldType = fieldInfo.FieldType.Name;开始的3行string fieldType = fieldInfo.FieldType.Name; with one line: string fieldType = fieldInfo.FieldType.GetElementType().Name; 一行: string fieldType = fieldInfo.FieldType.GetElementType().Name;

then replace your 1D array output with: 然后将您的一维数组输出替换为:

dynamic output = Array.CreateInstance(fieldInfo.FieldType.GetElementType(), arr.GetLength(0));

And your 2D array output with: 和您的2D数组输出具有:

dynamic output = Array.CreateInstance(fieldInfo.FieldType.GetElementType(), arr.GetLength(0), arr.GetLength(1));

BUT this is where it gets tricky: you will need to create a "type converter" function from the fieldInfo and use that in your for loops. 但是,这很棘手:您需要从fieldInfo创建一个“类型转换器”函数,并将其用于for循环中。 but you cannot 'unbox' and object to the desired type without knowing exactly what type you are converting to. 但是您不能在不确切知道要转换为哪种类型的情况下“拆箱”并反对所需的类型。 So unfortunately you will need case statements (or something similar). 因此,不幸的是,您将需要案例陈述(或类似的陈述)。 Basically the arrays are the problem, although now you only need 1 switch statement: 基本上,数组是问题所在,尽管现在您只需要1条switch语句:

var converter = TypeDescriptor.GetConverter(fieldInfo.FieldType.GetElementType());
for (int i = 0; i < arr.GetLength(0); i++)
{
    for (int j = 0; j < arr.GetLength(1); j++)
    {
        switch (fieldType)
        {
            case "Int32":
                output[i,j] = (int)converter.ConvertFromString(arr[i,j]);
                break;
            ...
        }
    }
}

fieldInfo.SetValue(target, output);

That is so much effort for what is essentially serializing. 对于本质上是序列化的工作,这是很大的努力。 Here is a better solution: 这是一个更好的解决方案:

  1. First get Json.NET from NuGet into your project 首先从NuGet获取Json.NET到您的项目中
  2. Replace the // 1. Class -> Dictionary section with string output = JsonConvert.SerializeObject(cylData); string output = JsonConvert.SerializeObject(cylData);替换// 1. Class -> Dictionary string output = JsonConvert.SerializeObject(cylData); // 1. Class -> Dictionary部分string output = JsonConvert.SerializeObject(cylData);
  3. To get the class back: JsonConvert.DeserializeObject<DCylinder>(output); 取回该类: JsonConvert.DeserializeObject<DCylinder>(output);

Solved in 2 lines of code :) 解决了两行代码:)


Anything else and you should post it in Code Review . 除此之外,您应该将其发布在Code Review中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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