C# 如何在不进行手动类型转换的情况下调用 FieldInfo.SetValue 函数
C# How to call FieldInfo.SetValue function without manual type conversion
我想将数据-class 实例保存到数据库并从数据库加载它。我想自动生成 sql 命令。所以,我认为我需要使用 dictionary 来解决它。
参考我的老问题:How to convert 2D string array to 2D [int, double, bool, ..] array?
这样的工作流程。
- 将数据-class 实例转换为字典
。
- save/load字典to/fromtxt、数据库等
- 将字典转换为数据-class实例
我想我已经解决了这个问题。但我认为转换二维数组的方法仍然不是一个完美的方法。我想知道,当我调用 FieldInfo.SetValue 函数时,是否有另一种方法可以解决类型转换问题,而无需像我的解决方案那样使用 switch/case 状态。
帮助我简化代码。
数据class像这样
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()
{
}
}
主要代码如下
// 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();
和 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;
}
}
这都是我的代码的一部分。
您将需要 GetElementType
、Array.CreateInstance
、TypeDescriptor.GetConverter
和 dynamic
(可选)。您仍然需要相当多的代码来完成它,因为您需要处理异常和未知类型以及字符串中的错误。
要回答您的问题,您可以做 3 件事:
将以string fieldType = fieldInfo.FieldType.Name;
开头的3行替换为一行:string fieldType = fieldInfo.FieldType.GetElementType().Name;
然后将您的一维数组输出替换为:
dynamic output = Array.CreateInstance(fieldInfo.FieldType.GetElementType(), arr.GetLength(0));
你的二维数组输出:
dynamic output = Array.CreateInstance(fieldInfo.FieldType.GetElementType(), arr.GetLength(0), arr.GetLength(1));
但这就是它变得棘手的地方:您需要从 fieldInfo 创建一个 "type converter" 函数并在您的 for 循环中使用它。但是您不能 'unbox' 并在不知道要转换为哪种类型的情况下反对所需的类型。所以不幸的是,您将需要 case 语句(或类似的东西)。基本上数组是问题所在,尽管现在您只需要 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);
对于本质上是序列化的内容来说,付出了如此多的努力。这是一个更好的解决方案:
- 首先从 NuGet 获取 Json.NET 到您的项目中
- 将
// 1. Class -> Dictionary
部分替换为 string output = JsonConvert.SerializeObject(cylData);
- 要取回 class:
JsonConvert.DeserializeObject<DCylinder>(output);
两行代码解决:)
任何其他内容,您应该 post 在 Code Review.
中
我想将数据-class 实例保存到数据库并从数据库加载它。我想自动生成 sql 命令。所以,我认为我需要使用 dictionary
参考我的老问题:How to convert 2D string array to 2D [int, double, bool, ..] array?
这样的工作流程。
- 将数据-class 实例转换为字典
。 - save/load字典to/fromtxt、数据库等
- 将字典转换为数据-class实例
我想我已经解决了这个问题。但我认为转换二维数组的方法仍然不是一个完美的方法。我想知道,当我调用 FieldInfo.SetValue 函数时,是否有另一种方法可以解决类型转换问题,而无需像我的解决方案那样使用 switch/case 状态。
帮助我简化代码。
数据class像这样
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()
{
}
}
主要代码如下
// 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();
和 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;
}
}
这都是我的代码的一部分。
您将需要 GetElementType
、Array.CreateInstance
、TypeDescriptor.GetConverter
和 dynamic
(可选)。您仍然需要相当多的代码来完成它,因为您需要处理异常和未知类型以及字符串中的错误。
要回答您的问题,您可以做 3 件事:
将以string fieldType = fieldInfo.FieldType.Name;
开头的3行替换为一行:string fieldType = fieldInfo.FieldType.GetElementType().Name;
然后将您的一维数组输出替换为:
dynamic output = Array.CreateInstance(fieldInfo.FieldType.GetElementType(), arr.GetLength(0));
你的二维数组输出:
dynamic output = Array.CreateInstance(fieldInfo.FieldType.GetElementType(), arr.GetLength(0), arr.GetLength(1));
但这就是它变得棘手的地方:您需要从 fieldInfo 创建一个 "type converter" 函数并在您的 for 循环中使用它。但是您不能 'unbox' 并在不知道要转换为哪种类型的情况下反对所需的类型。所以不幸的是,您将需要 case 语句(或类似的东西)。基本上数组是问题所在,尽管现在您只需要 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);
对于本质上是序列化的内容来说,付出了如此多的努力。这是一个更好的解决方案:
- 首先从 NuGet 获取 Json.NET 到您的项目中
- 将
// 1. Class -> Dictionary
部分替换为string output = JsonConvert.SerializeObject(cylData);
- 要取回 class:
JsonConvert.DeserializeObject<DCylinder>(output);
两行代码解决:)
任何其他内容,您应该 post 在 Code Review.
中