2013-12-20 61 views
0

我正在一個邏輯,我已經從詞典鍵/值對填充複合類實例。複合類將被標記爲屬性,這些屬性將映射到字典中的鍵。 一個特定的要求是,如果C1類具有C2類型的屬性,但字典中沒有對要映射C2類屬性的對,那麼C2應該設置爲null。否則,如果至少有一個可映射的C2屬性,則C1的C2屬性必須實例化。 我已經寫了一個遞歸函數來實現這個邏輯。該要求不按預期工作。我正在使用一個標誌isInstanceValuePresent,它檢查是否至少可以映射C2的屬性之一。否則,這是錯誤的價值應該告訴我,我必須爲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

我甚至明白,如果有人可以幫助我優化功能。 – Lucifer

+0

邏輯如何失敗?它沒有執行,或者結果不如預期的那樣? – Daniel

回答

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) 
      { 

希望這會有所幫助。

+0

感謝@roadkill,您的解決方案非常完美。我非常感謝您爲解釋解決方案付出的努力。 – Lucifer