I want to save data-class instance to database and load it from database. and I want to generate sql command automatically. so, I think that I need to use dictionary< string, string> so that solve it.
refer my old question : How to convert 2D string array to 2D [int, double, bool, ..] array?
working process like these.
I think I have solved the problem anyway. but I think converting 2D array method is still not a perfect one. I wonder, when I call FieldInfo.SetValue function, is there another way to solve type conversion without using switch/case state like my solution.
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
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). 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;
with one line: 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:
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. 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:
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. Class -> Dictionary
section with string output = JsonConvert.SerializeObject(cylData);
JsonConvert.DeserializeObject<DCylinder>(output);
Solved in 2 lines of code :)
Anything else and you should post it in Code Review.