我正在开发一个逻辑,我从字典键/值对中填充复合类实例。复合类将使用属性进行标记,这些属性将根据字典中的键进行映射。 一个特定的要求是,如果类C1具有类型为C2的属性,但是字典中没有要映射的类C2的属性的对,那么C2应该设置为null。否则,如果至少存在可以映射的C2的单个属性,则必须实例化C1的C2属性。 我编写了一个递归函数来实现这个逻辑。该要求未按预期工作。我正在使用一个标志isInstanceValuePresent,它检查是否可以映射C2的至少一个属性。否则,它的假值应告诉我必须将null赋给C1类的C2属性。如果有人能帮助我理解逻辑失败的原因以及可能是正确的解决方案,我将非常感激。以下是静态递归方法:
/// <summary>
/// Populates the given instance object with the supplied source dictionary values
/// </summary>
/// <param name="modelInstance">The object whose properties are to be initialized with the data</param>
/// <param name="source">The source dictionary containing Schema(Keys) and corresponding Values</param>
private static void PopulateModelInstance(object modelInstance, IDictionary<string, string> source)
{
bool isInstanceValuePresent = false;
foreach (PropertyInfo propInfo in modelInstance.GetType().GetProperties())
{
//Identify Custom attribute
DataMappingKeyAttribute attribute = (DataMappingKeyAttribute)Attribute.GetCustomAttribute(propInfo, typeof(DataMappingKeyAttribute));
if (attribute != null && !string.IsNullOrEmpty(attribute.MappingKey))
{
if (propInfo.PropertyType.IsPrimitive || propInfo.PropertyType.Equals(typeof(string)))
{
string sourceKey = attribute.MappingKey;
if (source.ContainsKey(sourceKey))
{
isInstanceValuePresent = true;
// Get propInfo attribute value from Dictionary
//var propertySourceValue = source[(propInfo.PropertyType.GetCustomAttribute(typeof(DataMappingKeyAttribute)) as DataMappingKeyAttribute).MappingKey];
string sourceValue = source[attribute.MappingKey];
// Set propInfo value on the model instance
if (CanChangeType(sourceValue, propInfo.PropertyType) && propInfo.CanWrite && (!propInfo.PropertyType.IsClass || propInfo.PropertyType.Equals(typeof(string))))
propInfo.SetValue(modelInstance, Convert.ChangeType(sourceValue, propInfo.PropertyType), null);
}
}
}
if (propInfo.PropertyType.IsClass && !propInfo.PropertyType.Equals(typeof(string)) && propInfo.CanWrite)
{
isInstanceValuePresent = false;
object referenceTypeInstance = Activator.CreateInstance(propInfo.PropertyType);
PopulateModelInstance(referenceTypeInstance, source);
if (isInstanceValuePresent == false)
{
propInfo.SetValue(modelInstance, null, null);
referenceTypeInstance = null;
}
else
{
propInfo.SetValue(modelInstance, referenceTypeInstance, null);
}
}
}
}
答案 0 :(得分:1)
您的代码的一个主要问题是使用变量 isInstanceValuePresent 。在递归调用 PopulateModelInstance 之前,将变量设置为false,然后在方法返回后测试该值。不幸的是,这个变量是一个局部变量,驻留在堆栈上,因此对每个调用都是本地变量。它不会反映递归调用中设置的值。
我建议您如何更改方法。您可以传入要填充的对象的类型,而不是传入要填充的对象。使用已经实现的相同逻辑,如果找到可以设置的属性值,则只能实例化此类型的对象。然后该方法传回实例。如果未找到任何属性设置,则该方法将返回 null 。
private static Object CreateAndPopulateModelInstance(Type modelInstanceType, IDictionary<string, string> source)
{
// this variable will hold the reference to the instance that is to be
// populated. It will only hold a value, if a property is found that
// can be populated.
Object modelInstance = null;
foreach (PropertyInfo propInfo in modelInstanceType.GetProperties())
{
//Identify Custom attribute
DataMappingKeyAttribute attribute = DataMappingKeyAttribute)Attribute.GetCustomAttribute(propInfo, typeof(DataMappingKeyAttribute));
if (attribute != null && !string.IsNullOrEmpty(attribute.MappingKey))
{
if (propInfo.PropertyType.IsPrimitive || propInfo.PropertyType.Equals(typeof(string)))
{
string sourceKey = attribute.MappingKey;
if (source.ContainsKey(sourceKey))
{
// Get propInfo attribute value from Dictionary
//var propertySourceValue = source[(propInfo.PropertyType.GetCustomAttribute(typeof(DataMappingKeyAttribute)) as DataMappingKeyAttribute).MappingKey];
string sourceValue = source[attribute.MappingKey];
// Set propInfo value on the model instance
if (CanChangeType(sourceValue, propInfo.PropertyType) && propInfo.CanWrite && (!propInfo.PropertyType.IsClass || propInfo.PropertyType.Equals(typeof(string))))
{
// create instance if necessary
if (modelInstance == null)
modelInstance = Activator.CreateInstance(modelInstanceType);
propInfo.SetValue(modelInstance, Convert.ChangeType(sourceValue, propInfo.PropertyType), null);
}
}
}
}
else if (propInfo.PropertyType.IsClass && !propInfo.PropertyType.Equals(typeof(string)) && propInfo.CanWrite)
{
Object propertyValue = CreateAndPopulateModelInstance(propInfo.PropertyType, source);
if (propertyValue != null)
{
// create instance if necessary
if (modelInstance == null)
modelInstance = Activator.CreateInstance(modelInstanceType);
// set property value
propInfo.SetValue(modelInstance, propertyValue, null);
}
}
}
return modelInstance;
}
我重命名了方法以反映创建对象的事实(如果需要)。 在设置属性之前,方法检查是否已经实例化了modelInstance,如果没有实例化传入类型的对象。
如果方法传回 null ,您就知道您的属性值无法实例化,因为它没有任何可以初始化的属性。无论递归的深度如何,这都应该有效。
我没有测试过这段代码(甚至没有编译过它),因此可能存在语法错误。逻辑应该没问题。
您必须更改对此方法的初始调用,或者添加初始化modelInstance变量的第三个参数,如下所示:
private static Object CreateAndPopulateModelInstance(Object instance, Type modelInstanceType, IDictionary<string, string> source)
{
// this variable will hold the reference to the instance that is to be
// populated. It will only hold a value, if a property is found that
// can be populated.
Object modelInstance = instance;
递归调用将如下所示:
Object propertyValue = CreateAndPopulateModelInstance(null, propInfo.PropertyType, source);
if (propertyValue != null)
{
希望这有帮助。