2012-01-27 8 views
8

मुझे सी # में 2 गतिशील वस्तुओं को "विलय" करने की आवश्यकता है। स्टैक एक्सचेंज पर जो कुछ मैंने पाया है, वह केवल गैर-रिकर्सिव विलय को कवर करता है। लेकिन मैं ऐसा कुछ ढूंढ रहा हूं जो रिकर्सिव या गहरी विलय करता है, jQuery's $.extend(obj1, obj2) फ़ंक्शन जैसा ही है।सी # गतिशील/विस्तारित वस्तुओं के गहरे/घोंसले/पुनरावर्ती विलय

दो सदस्यों की टक्कर होने पर, निम्नलिखित नियम लागू करना चाहिए:

  • प्रकार बेमेल, एक अपवाद फेंका जाना चाहिए और विलय निरस्त किया गया है। अपवाद: obj2 मान शायद शून्य, इस मामले में मान & obj1 का प्रकार उपयोग किया जाता है।
  • तुच्छ प्रकारों के लिए (मूल्य प्रकार + स्ट्रिंग) obj1 मूल्यों हमेशा
  • पसंद कर रहे हैं गैर तुच्छ प्रकार के लिए, निम्न नियम लागू होते हैं:
    • IEnumerable & IEnumberables<T> बस विलय कर रहे हैं (शायद .Concat()?)
    • IDictionary & IDictionary<TKey,TValue> विलय कर रहे हैं; obj1 कुंजी टक्कर पर पूर्वता है
    • Expando & Expando[] प्रकार रिकर्सिवली विलय कर दिया जाना चाहिए, जबकि expando [] हमेशा एक ही प्रकार तत्व होगा केवल
    • एक मान सकते हैं वहाँ संग्रह (IEnumerabe & IDictionary)
    • के भीतर कोई expando वस्तुओं रहे हैं
  • सभी अन्य प्रकार खारिज किया जा सकता है और जिसके परिणामस्वरूप गतिशील वस्तु

यहाँ में मौजूद होने की ज़रूरत नहीं एक संभव मर्ज का एक उदाहरण है:

dynamic DefaultConfig = new { 
    BlacklistedDomains = new string[] { "domain1.com" }, 
    ExternalConfigFile = "blacklist.txt", 
    UseSockets = new[] { 
     new { IP = "127.0.0.1", Port = "80"}, 
     new { IP = "127.0.0.2", Port = "8080" } 
    } 
}; 

dynamic UserSpecifiedConfig = new { 
    BlacklistedDomain = new string[] { "example1.com" }, 
    ExternalConfigFile = "C:\\my_blacklist.txt" 
}; 

var result = Merge (UserSpecifiedConfig, DefaultConfig); 
// result should now be equal to: 
var result_equal = new { 
    BlacklistedDomains = new string[] { "domain1.com", "example1.com" }, 
    ExternalConfigFile = "C:\\my_blacklist.txt", 
    UseSockets = new[] { 
     new { IP = "127.0.0.1", Port = "80"}, 
     new { IP = "127.0.0.2", Port = "8080" } 
    } 
}; 

कोई विचार यह कैसे करना है?

+0

क्या आपको रिकर्सिव तर्क या प्रकारों को समझने के लिए आवश्यक वास्तविक कॉल के साथ समस्याएं हैं। –

उत्तर

3

ठीक है, यह थोड़ा सा लंबा है लेकिन एक नज़र डालें। यह Reflection.Emit का उपयोग कर एक कार्यान्वयन है।

मेरे लिए ओपन इश्यू एक ToString() ओवरराइड को कार्यान्वित करने का तरीका है ताकि आप एक स्ट्रिंग तुलना कर सकें। क्या ये मान कॉन्फ़िगरेशन फ़ाइल या कुछ से आ रहे हैं? यदि वे जेएसओएन प्रारूप में हैं तो आप सोचते हैं कि जेसनएसरियललाइज़र का उपयोग करने से भी बदतर हो सकता है। इस पर निर्भर है कि आपको क्या चाहिए।

साथ ही आप Reflection.Emit बकवास से छुटकारा पाने के, पाश के तल पर expando वस्तु इस्तेमाल कर सकते हैं:

var result = new ExpandoObject(); 
var resultDict = result as IDictionary<string, object>; 
foreach (string key in resVals.Keys) 
{ 
    resultDict.Add(key, resVals[key]); 
} 
return result; 

मैं नहीं पार्स करने के लिए गंदा कोड के चारों ओर एक रास्ता देख सकते हैं हालांकि मूल वस्तु पेड़, तुरंत नहीं। मैं इस पर कुछ और राय सुनना चाहता हूं। डीएलआर मेरे लिए अपेक्षाकृत नया आधार है।

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Reflection; 
using System.Reflection.Emit; 
using System.Runtime.CompilerServices; 
using System.Threading; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      dynamic DefaultConfig = new 
      { 
       BlacklistedDomains = new string[] { "domain1.com" }, 
       ExternalConfigFile = "blacklist.txt", 
       UseSockets = new[] { 
        new { IP = "127.0.0.1", Port = "80" }, 
        new { IP = "127.0.0.2", Port = "8080" } 
       } 
      }; 

      dynamic UserSpecifiedConfig = new 
      { 
       BlacklistedDomains = new string[] { "example1.com" }, 
       ExternalConfigFile = "C:\\my_blacklist.txt" 
      }; 

      var result = Merge(UserSpecifiedConfig, DefaultConfig); 

      // result should now be equal to: 

      var result_equal = new 
      { 
       BlacklistedDomains = new string[] { "domain1.com", "example1.com" }, 
       ExternalConfigFile = "C:\\my_blacklist.txt", 
       UseSockets = new[] {   
        new { IP = "127.0.0.1", Port = "80"},   
        new { IP = "127.0.0.2", Port = "8080" }  
       } 
      }; 
      Debug.Assert(result.Equals(result_equal)); 
     } 

     /// <summary> 
     /// Merge the properties of two dynamic objects, taking the LHS as primary 
     /// </summary> 
     /// <param name="lhs"></param> 
     /// <param name="rhs"></param> 
     /// <returns></returns> 
     static dynamic Merge(dynamic lhs, dynamic rhs) 
     { 
      // get the anonymous type definitions 
      Type lhsType = ((Type)((dynamic)lhs).GetType()); 
      Type rhsType = ((Type)((dynamic)rhs).GetType()); 

      object result = new { }; 
      var resProps = new Dictionary<string, PropertyInfo>(); 
      var resVals = new Dictionary<string, object>(); 

      var lProps = lhsType.GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name); 
      var rProps = rhsType.GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name); 


      foreach (string leftPropKey in lProps.Keys) 
      { 
       var lPropInfo = lProps[leftPropKey]; 
       resProps.Add(leftPropKey, lPropInfo); 
       var lhsVal = Convert.ChangeType(lPropInfo.GetValue(lhs, null), lPropInfo.PropertyType); 
       if (rProps.ContainsKey(leftPropKey)) 
       { 
        PropertyInfo rPropInfo; 
        rPropInfo = rProps[leftPropKey]; 
        var rhsVal = Convert.ChangeType(rPropInfo.GetValue(rhs, null), rPropInfo.PropertyType); 
        object setVal = null; 

        if (lPropInfo.PropertyType.IsAnonymousType()) 
        { 
         setVal = Merge(lhsVal, rhsVal); 
        } 
        else if (lPropInfo.PropertyType.IsArray) 
        { 
         var bound = ((Array) lhsVal).Length + ((Array) rhsVal).Length; 
         var cons = lPropInfo.PropertyType.GetConstructor(new Type[] { typeof(int) }); 
         dynamic newArray = cons.Invoke(new object[] { bound }); 
         //newArray = ((Array)lhsVal).Clone(); 
         int i=0; 
         while (i < ((Array)lhsVal).Length) 
         { 
          newArray[i] = lhsVal[i]; 
          i++; 
         } 
         while (i < bound) 
         { 
          newArray[i] = rhsVal[i - ((Array)lhsVal).Length]; 
          i++; 
         } 
         setVal = newArray; 
        } 
        else 
        { 
         setVal = lhsVal == null ? rhsVal : lhsVal; 
        } 
        resVals.Add(leftPropKey, setVal); 
       } 
       else 
       { 
        resVals.Add(leftPropKey, lhsVal); 
       } 
      } 
      foreach (string rightPropKey in rProps.Keys) 
      { 
       if (lProps.ContainsKey(rightPropKey) == false) 
       { 
        PropertyInfo rPropInfo; 
        rPropInfo = rProps[rightPropKey]; 
        var rhsVal = rPropInfo.GetValue(rhs, null); 
        resProps.Add(rightPropKey, rPropInfo); 
        resVals.Add(rightPropKey, rhsVal); 
       } 
      } 

      Type resType = TypeExtensions.ToType(result.GetType(), resProps); 

      result = Activator.CreateInstance(resType); 

      foreach (string key in resVals.Keys) 
      { 
       var resInfo = resType.GetProperty(key); 
       resInfo.SetValue(result, resVals[key], null); 
      } 
      return result; 
     } 
    } 
} 

public static class TypeExtensions 
{ 
    public static Type ToType(Type type, Dictionary<string, PropertyInfo> properties) 
    { 
     AppDomain myDomain = Thread.GetDomain(); 
     Assembly asm = type.Assembly; 
     AssemblyBuilder assemblyBuilder = 
      myDomain.DefineDynamicAssembly(
      asm.GetName(), 
      AssemblyBuilderAccess.Run 
     ); 
     ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(type.Module.Name); 
     TypeBuilder typeBuilder = moduleBuilder.DefineType(type.Name,TypeAttributes.Public); 

     foreach (string key in properties.Keys) 
     { 
      string propertyName = key; 
      Type propertyType = properties[key].PropertyType; 

      FieldBuilder fieldBuilder = typeBuilder.DefineField(
       "_" + propertyName, 
       propertyType, 
       FieldAttributes.Private 
      ); 

      PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
       propertyName, 
       PropertyAttributes.HasDefault, 
       propertyType, 
       new Type[] { } 
      ); 
      // First, we'll define the behavior of the "get" acessor for the property as a method. 
      MethodBuilder getMethodBuilder = typeBuilder.DefineMethod(
       "Get" + propertyName, 
       MethodAttributes.Public, 
       propertyType, 
       new Type[] { } 
      ); 

      ILGenerator getMethodIL = getMethodBuilder.GetILGenerator(); 

      getMethodIL.Emit(OpCodes.Ldarg_0); 
      getMethodIL.Emit(OpCodes.Ldfld, fieldBuilder); 
      getMethodIL.Emit(OpCodes.Ret); 

      // Now, we'll define the behavior of the "set" accessor for the property. 
      MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
       "Set" + propertyName, 
       MethodAttributes.Public, 
       null, 
       new Type[] { propertyType } 
      ); 

      ILGenerator custNameSetIL = setMethodBuilder.GetILGenerator(); 

      custNameSetIL.Emit(OpCodes.Ldarg_0); 
      custNameSetIL.Emit(OpCodes.Ldarg_1); 
      custNameSetIL.Emit(OpCodes.Stfld, fieldBuilder); 
      custNameSetIL.Emit(OpCodes.Ret); 

      // Last, we must map the two methods created above to our PropertyBuilder to 
      // their corresponding behaviors, "get" and "set" respectively. 
      propertyBuilder.SetGetMethod(getMethodBuilder); 
      propertyBuilder.SetSetMethod(setMethodBuilder); 
     } 

     //MethodBuilder toStringMethodBuilder = typeBuilder.DefineMethod(
     // "ToString", 
     // MethodAttributes.Public, 
     // typeof(string), 
     // new Type[] { } 
     //); 

     return typeBuilder.CreateType(); 
    } 
    public static Boolean IsAnonymousType(this Type type) 
    { 
     Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(
      typeof(CompilerGeneratedAttribute), false).Count() > 0; 
     Boolean nameContainsAnonymousType = 
      type.FullName.Contains("AnonymousType"); 
     Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType; 
     return isAnonymousType; 
    } 
} 
संबंधित मुद्दे