通过反射 c# 生成嵌套 类 的实例
Generating instances of nested classes via reflection c#
我有一个 DAL,它可以访问多个 CSV 文件,这些文件不断更新其格式,并不断添加新格式。
CSV 文件中的每一行都代表系统中的一个 class,但由于格式众多,输入通过两步处理。
步骤 1. 将 CSV 文件中的行转换为值列表。将此值列表转换为 "raw" 对象。
此原始对象稍后将映射到业务逻辑对象,因此所有原始对象都将转换为业务逻辑对象。
但是,由于某些 csv 行严重倾向于使用嵌套 class 生成 class,我的通用映射器目前用处不大。
映射对象,是一个属性,其索引位置由属性上的属性分配的值字段。
public static T BuildRawObject(string[] values)
{
T target = (T) Activator.CreateInstance(typeof(T), new object[] { });
Type targetInfo = target.GetType();
foreach (PropertyInfo pi in targetInfo.GetProperties())
{
if (!pi.PropertyType.IsValueType)
{
if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj1)))
{
var result = BuildRawObject2<NestedObj1>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if(pi.PropertyType.IsAssignableFrom(typeof(NestedObj2)))
{
var result = BuildRawObject2<NestedObj2>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj3)))
{
var result = BuildRawObject2<NestedObj3>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj5)))
{
var result = BuildRawObject2<NestedObj5>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj6)))
{
var result = BuildRawObject2<NestedObj6>(values);
try
{
pi.SetValue(target, result);
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj7)))
{
var result = BuildRawObject2<NestedObj7>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else
{
Console.WriteLine("An unrecognized class exists in the raw object. This will not be mapped to: " + pi.Name);
}
}
else
{
Mapping mapping = pi.GetCustomAttribute<Mapping>();
if (mapping != null)
{
values[mapping.SourceIndex] = EmptyStringValuesWhenEmpty(values[mapping.SourceIndex]);
if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
{
bool valid = DateTime.TryParse(values[mapping.SourceIndex], out DateTime dateTime);
if (valid)
{
pi.SetValue(target, dateTime, null);
}
else
{
Console.WriteLine("Value: " + values[mapping.SourceIndex] + " on position[" +
mapping.SourceIndex + "] was not a valid date time. Value is required.");
}
}
else
{
try
{
pi.SetValue(target, GenericConverter(values[mapping.SourceIndex], pi.PropertyType), null);
}
catch (Exception e)
{
Console.WriteLine("Exception on index: " + mapping.SourceIndex + " contained value: " +
values[mapping.SourceIndex] + " was attempted for property type: " +
pi.PropertyType +
"Parse was not possible. Either input file is in wrong format, or RawInputObject has incorect property");
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
}
else
{
Console.WriteLine("Mapping to field: " + pi.Name +
" was skipped due to missing mapping attribute definition.");
}
}
}
return target;
}
当我尝试为 "Raw return object".
的嵌套 classes 赋值时,问题就来了
理想情况下,我更愿意将整个事情变成一个递归方法。
但是,父 class 与嵌套 class 的类型不同。
所以我必须生成一个新方法,它与父 class 相同,但接受嵌套的 classes
但是,从理论上讲,生成可能出现在泛型方法中的命名类型对象的无限列表是……错误的。
我宁愿简单地让程序从传递给方法的任何专门的嵌套 class 进行推断,以确定方法的 return 类型。
这可能吗?我不知道怎么办。
如果我的问题不清楚,或者您对我如何更好地提出问题有建议,请告诉我。如果缺少细节。请指教,我会提供。
编辑:提供解决方案后的方法
public static object BuildRawObject(Type objectType, string[] values)
{
dynamic target = Activator.CreateInstance(objectType, new object[] { });
Type targetInfo = target.GetType();
foreach (PropertyInfo pi in targetInfo.GetProperties())
{
if (pi.PropertyType.GetInterfaces().AsEnumerable().Contains(typeof(IRawObjectBase)) || pi.PropertyType.GetInterfaces().AsEnumerable().Contains(typeof(IRawNestedObjectBase)))
{
var result = BuildRawObject(pi.PropertyType, values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else
{
Mapping mapping = pi.GetCustomAttribute<Mapping>();
if (mapping != null)
{
values[mapping.SourceIndex] = EmptyStringValuesWhenEmpty(values[mapping.SourceIndex]);
if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
{
bool valid = DateTime.TryParse(values[mapping.SourceIndex], out DateTime dateTime);
if (valid)
{
pi.SetValue(target, dateTime, null);
}
else
{
Console.WriteLine("Value: " + values[mapping.SourceIndex] + " on position[" +
mapping.SourceIndex + "] was not a valid date time. Value is required.");
}
}
else
{
try
{
pi.SetValue(target, GenericConverter(values[mapping.SourceIndex], pi.PropertyType), null);
}
catch (Exception e)
{
Console.WriteLine("Exception on index: " + mapping.SourceIndex + " contained value: " +
values[mapping.SourceIndex] + " was attempted for property type: " +
pi.PropertyType +
"Parse was not possible. Either input file is in wrong format, or RawInputObject has incorect property");
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
}
else
{
Console.WriteLine("Mapping to field: " + pi.Name +
" was skipped due to missing mapping attribute definition.");
}
}
}
return target;
}
在这种情况下,似乎不必要地使用泛型会让您感到厌烦。如果您将签名更改为
static object BuildRawObject(Type objectType, string[] values)
您将能够直接在那里传递 属性 类型,而无需对 属性 类型进行大量检查:
if (propertyIsNotPrimitiveType) {
var result = BuildRawObject(pi.PropertyType, values);
}
为方便起见,您仍然可以保留通用重载:
static T BuildRawObject<T>(string[] values) {
return (T) BuildRawObject(typeof(T), values);
}
我有一个 DAL,它可以访问多个 CSV 文件,这些文件不断更新其格式,并不断添加新格式。
CSV 文件中的每一行都代表系统中的一个 class,但由于格式众多,输入通过两步处理。
步骤 1. 将 CSV 文件中的行转换为值列表。将此值列表转换为 "raw" 对象。
此原始对象稍后将映射到业务逻辑对象,因此所有原始对象都将转换为业务逻辑对象。
但是,由于某些 csv 行严重倾向于使用嵌套 class 生成 class,我的通用映射器目前用处不大。
映射对象,是一个属性,其索引位置由属性上的属性分配的值字段。
public static T BuildRawObject(string[] values)
{
T target = (T) Activator.CreateInstance(typeof(T), new object[] { });
Type targetInfo = target.GetType();
foreach (PropertyInfo pi in targetInfo.GetProperties())
{
if (!pi.PropertyType.IsValueType)
{
if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj1)))
{
var result = BuildRawObject2<NestedObj1>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if(pi.PropertyType.IsAssignableFrom(typeof(NestedObj2)))
{
var result = BuildRawObject2<NestedObj2>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj3)))
{
var result = BuildRawObject2<NestedObj3>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj5)))
{
var result = BuildRawObject2<NestedObj5>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj6)))
{
var result = BuildRawObject2<NestedObj6>(values);
try
{
pi.SetValue(target, result);
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj7)))
{
var result = BuildRawObject2<NestedObj7>(values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else
{
Console.WriteLine("An unrecognized class exists in the raw object. This will not be mapped to: " + pi.Name);
}
}
else
{
Mapping mapping = pi.GetCustomAttribute<Mapping>();
if (mapping != null)
{
values[mapping.SourceIndex] = EmptyStringValuesWhenEmpty(values[mapping.SourceIndex]);
if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
{
bool valid = DateTime.TryParse(values[mapping.SourceIndex], out DateTime dateTime);
if (valid)
{
pi.SetValue(target, dateTime, null);
}
else
{
Console.WriteLine("Value: " + values[mapping.SourceIndex] + " on position[" +
mapping.SourceIndex + "] was not a valid date time. Value is required.");
}
}
else
{
try
{
pi.SetValue(target, GenericConverter(values[mapping.SourceIndex], pi.PropertyType), null);
}
catch (Exception e)
{
Console.WriteLine("Exception on index: " + mapping.SourceIndex + " contained value: " +
values[mapping.SourceIndex] + " was attempted for property type: " +
pi.PropertyType +
"Parse was not possible. Either input file is in wrong format, or RawInputObject has incorect property");
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
}
else
{
Console.WriteLine("Mapping to field: " + pi.Name +
" was skipped due to missing mapping attribute definition.");
}
}
}
return target;
}
当我尝试为 "Raw return object".
的嵌套 classes 赋值时,问题就来了理想情况下,我更愿意将整个事情变成一个递归方法。 但是,父 class 与嵌套 class 的类型不同。 所以我必须生成一个新方法,它与父 class 相同,但接受嵌套的 classes
但是,从理论上讲,生成可能出现在泛型方法中的命名类型对象的无限列表是……错误的。 我宁愿简单地让程序从传递给方法的任何专门的嵌套 class 进行推断,以确定方法的 return 类型。 这可能吗?我不知道怎么办。
如果我的问题不清楚,或者您对我如何更好地提出问题有建议,请告诉我。如果缺少细节。请指教,我会提供。
编辑:提供解决方案后的方法
public static object BuildRawObject(Type objectType, string[] values)
{
dynamic target = Activator.CreateInstance(objectType, new object[] { });
Type targetInfo = target.GetType();
foreach (PropertyInfo pi in targetInfo.GetProperties())
{
if (pi.PropertyType.GetInterfaces().AsEnumerable().Contains(typeof(IRawObjectBase)) || pi.PropertyType.GetInterfaces().AsEnumerable().Contains(typeof(IRawNestedObjectBase)))
{
var result = BuildRawObject(pi.PropertyType, values);
try
{
pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
}
catch (Exception e)
{
Console.WriteLine("Exception on property: " + pi.PropertyType +
" was bit not to set built instance on property. Instace was: " + result);
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
else
{
Mapping mapping = pi.GetCustomAttribute<Mapping>();
if (mapping != null)
{
values[mapping.SourceIndex] = EmptyStringValuesWhenEmpty(values[mapping.SourceIndex]);
if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
{
bool valid = DateTime.TryParse(values[mapping.SourceIndex], out DateTime dateTime);
if (valid)
{
pi.SetValue(target, dateTime, null);
}
else
{
Console.WriteLine("Value: " + values[mapping.SourceIndex] + " on position[" +
mapping.SourceIndex + "] was not a valid date time. Value is required.");
}
}
else
{
try
{
pi.SetValue(target, GenericConverter(values[mapping.SourceIndex], pi.PropertyType), null);
}
catch (Exception e)
{
Console.WriteLine("Exception on index: " + mapping.SourceIndex + " contained value: " +
values[mapping.SourceIndex] + " was attempted for property type: " +
pi.PropertyType +
"Parse was not possible. Either input file is in wrong format, or RawInputObject has incorect property");
Console.WriteLine("Exception mesessage was: " + e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
}
}
}
}
else
{
Console.WriteLine("Mapping to field: " + pi.Name +
" was skipped due to missing mapping attribute definition.");
}
}
}
return target;
}
在这种情况下,似乎不必要地使用泛型会让您感到厌烦。如果您将签名更改为
static object BuildRawObject(Type objectType, string[] values)
您将能够直接在那里传递 属性 类型,而无需对 属性 类型进行大量检查:
if (propertyIsNotPrimitiveType) {
var result = BuildRawObject(pi.PropertyType, values);
}
为方便起见,您仍然可以保留通用重载:
static T BuildRawObject<T>(string[] values) {
return (T) BuildRawObject(typeof(T), values);
}