2012-08-02 20 views
6

हमें उदाहरण के लिए एक वर्ग परिभाषा के साथ शुरू करते हैं:संघ

{"Robby", "Goki", 12, 8} 
{"Bobby", "Goki", 10, 8} 
{"Sobby", "Goki", 10, 8} 
:

public class Person 
{ 
    public string FirstName; 
    public string LastName; 
    public int Age; 
    public int Grade; 
} 

अब मैं एक List<Person> बुलाया people 3 वस्तुओं से युक्त है मान लें

{null, "Goki", -1, 8} 
:

क्या मैं देख रहा हूँ किसी तरह निम्नलिखित एकल Person वस्तु पुनः प्राप्त करने के लिए है

जहां सभी ऑब्जेक्ट्स में समान फ़ील्ड अपना मान बनाए रखते हैं, जबकि फ़ील्ड जिनमें एकाधिक मान हैं, अमान्य मान के साथ प्रतिस्थापित किए जाते हैं।

Person unionMan = new Person(); 
if (people.Select(p => p.FirstName).Distinct().Count() == 1) 
    unionMan.FirstName = people[0].FirstName; 
if (people.Select(p => p.LastName).Distinct().Count() == 1) 
    unionMan.LastName = people[0].LastName; 
if (people.Select(p => p.Age).Distinct().Count() == 1) 
    unionMan.Age = people[0].Age; 
if (people.Select(p => p.Grade).Distinct().Count() == 1) 
    unionMan.Grade = people[0].Grade; 

दुर्भाग्य से, वास्तविक व्यापार वस्तु चार से कई अधिक सदस्य हैं और इस किसी और को देखने के लिए पहली बार के लिए दोनों लिखने के लिए कठिन और भारी है:

मेरी पहली सोचा शामिल थे।

मैं भी एक पाश में इन दोहराए नियंत्रण और कार्य डाल करने के लिए प्रतिबिंब का इस्तेमाल कर रही किसी भी तरह माना:

string[] members = new string[] { "FirstName", "LastName", "Age", "Grade" }; 
foreach (string member in members) 
{ 
    if (people.Select(p => p.**member**).Distinct().Count() == 1) 
     unionMan.**member** = people[0].**member**; 
} 

जहां ** सदस्य ** होगा लेकिन प्रतिबिंब पुनः प्राप्ति और भंडारण की अनुमति होगी उस विशेष सदस्य (मान लीजिए कि यह संभव है)।

जबकि पहला समाधान काम करेगा, और दूसरा मैं मानता हूं कि काम करेगा, क्या किसी के पास इस समस्या का बेहतर वैकल्पिक समाधान है? यदि नहीं, ऊपर वर्णित प्रतिबिंब का उपयोग करना संभव है?

+1

खैर , आपके दूसरे के विचार को काम करना चाहिए, लेकिन वास्तव में दिखाए गए कोड के साथ नहीं! वैसे, आपके पास अपने "अमान्य" मान वाले व्यक्ति के लिए डिफ़ॉल्ट कन्स्ट्रक्टर होना चाहिए। –

उत्तर

5

यह विशिष्ट सदस्यों की गणना करने के लिए सभी मूल्यों का एक अलग करने में अक्षम है। आपके पास शॉर्टकट परिदृश्य है जिसमें में किसी भी में किसी आइटम को बाद में आइटम्स के समान मूल्य नहीं मिला है, जिसमें पहले आइटम के सदस्य के समान मूल्य नहीं है, जिसका अर्थ है कि आपके पास उस कॉलम के लिए एक अमान्य स्थिति है।

कुछ इस तरह, काम करना चाहिए, हालांकि अधिक काम किया जाना है, तो के किसी सदस्य सरणियों कर रहे हैं, पुनरावर्ती मूल्यांकन या अन्य अधिक जटिल तर्क की जरूरत की आवश्यकता होगी (ध्यान दें मैं इस परीक्षण नहीं किया):

public static T UnionCombine<T>(this IEnumerable<T> values) where T : new() { 
    var newItem = new T(); 
    var properties = typeof(T).GetProperties(); 
    for (var prop in properties) { 
     var pValueFirst = prop.GetValue(values.First(), null); 
     var useDefaultValue = values.Skip(1).Any(v=>!(Object.Equals(pValueFirst, prop.GetValue(v, null)))); 
     if (!useDefaultValue) prop.SetValue(newItem, pValueFirst, null); 
    } 
    return newItem; 
} 
+1

+1 शानदार विचार का उपयोग नहीं करना है, लेकिन पहले मान पर जांच करें :) – digEmAll

+0

+1 बहुत उत्साहजनक। आप बस 'इस' शब्द को अपने पैरामीटर घोषणा में जोड़ना भूल गए हैं। –

+0

@jmh_gr धन्यवाद, तय –

2

आपका अंतिम विचार मेरे लिए बहुत अच्छा लगता है, कुछ इस तरह:

List<Person> persons = new List<Person>() 
{ 
    new Person(){ FirstName="Robby", LastName="Goki", Age=12, Grade=8}, 
    new Person(){ FirstName="Bobby", LastName="Goki", Age=10, Grade=8}, 
    new Person(){ FirstName="Sobby", LastName="Goki", Age=10, Grade=8}, 
}; 

var properties = typeof(Person).GetProperties(); 

var unionMan = new Person(); 
foreach (var propertyInfo in properties) 
{ 
    var values = persons.Select(x => propertyInfo.GetValue(x, null)).Distinct(); 
    if (values.Count() == 1) 
     propertyInfo.SetValue(unionMan, propertyInfo.GetValue(persons.First(), null), null); 
} 

टिप्पणियों के एक जोड़े:

  • अपने वर्ग के सदस्यों और गुण के रूप में परिभाषित किया जाना चाहिए सार्वजनिक नहीं सदस्यों और दोनों मिल और सेट एक्सेसर सार्वजनिक होना चाहिए
  • डिफ़ॉल्ट कन्स्ट्रक्टर को "अमान्य" मानों को परिभाषित करना चाहिए (जैसा कि @ राफेल अलाथॉस द्वारा सही ढंग से सुझाया गया है)

हां, वर्ग व्यक्ति इस प्रकार दिखाई देगा:

public class Person 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 
    public int Grade { get; set; } 
    public Person() 
    { 
     this.FirstName = null; 
     this.LastName = null; 
     this.Age = -1; 
     this.Grade = -1; 
    } 
} 
+2

डिफ़ॉल्ट कन्स्ट्रक्टर पर शानदार बिंदु। 'PropertyInfo.GetGetMethod()' और 'PropertyInfo.GetSetMethod()' 'PropertyInfo.GetValue()' और 'PropertyInfo.SetValue()' पर 'PropertyInfo.GetGetMethod()' का उपयोग करने का कोई कारण है? यदि नहीं, तो मुझे लगता है कि उत्तरार्द्ध थोड़ा कम वर्बोज़ है। –

+0

@ जोनसेन्चिना: बस मैं उन शॉर्टकट्स को भूल गया: पी संकेत के लिए धन्यवाद;) – digEmAll

+0

आह हाँ, यह बस एक उदाहरण वर्ग है जिसे मैंने एसओ संपादक में एक साथ फेंक दिया, वास्तविक वस्तु में वास्तव में एक निर्माता है जो प्रत्येक सदस्य को " अवैध "डिफ़ॉल्ट रूप से स्थिति। जहां तक ​​संपत्ति बनाम सदस्य हैं, वास्तविक वस्तु जिसके साथ मैं काम कर रहा हूं उसे एक बाहरी असेंबली में परिभाषित किया गया है जिसे मैं निर्भरता के मुद्दों के कारण बदल नहीं सकता। – Dan

1

अद्यतन: आप व्यक्ति वर्ग पर नियंत्रण की जरूरत नहीं है के बाद से, और राज्य बल्कि प्रॉपर्टी के अलावा, सार्वजनिक क्षेत्रों में परिभाषित किया गया है, मैं इस पते को हल करने के लिए समाधान अद्यतन किया है।

मैं प्रतिबिंब का उपयोग करने की सिफारिश करता हूं। आप अपने LINQ क्वेरी में प्रत्येक प्रविष्टि के लिए इसे प्राप्त करने के बजाय FieldInfo (या PropertyInfo) समय से पहले ऑब्जेक्ट प्राप्त करना चाहते हैं। आप उन्हें Type.GetField और Type.GetProperty का उपयोग करके प्राप्त कर सकते हैं।एक बार आपके पास हो जाने के बाद, आप बस FieldInfo/PropertyInfo.GetValue और FieldInfo/PropertyInfo.SetValue का उपयोग कर सकते हैं।

उदाहरण के लिए:

Type personType = typeof(Person); 
foreach(string member in members) 
{ // Get Fields via Reflection 
    FieldInfo field = peopleType.GetField(member); 
    if(field != null) 
    { 
     if (people.Select(p => field.GetValue(p, null)).Distinct().Count() == 1) 
     { 
      field.SetValue(unionMan, field.GetValue(people[0], null), null); 
     } 
    } 
    else // If member is not a field, check if it's a property instead 
    { // Get Properties via Reflection 
     PropertyInfo prop = peopleType.GetProperty(member); 
     if(prop != null) 
     { 
      if (people.Select(p => prop.GetValue(p, null)).Distinct().Count() == 1) 
      { 
       prop.SetValue(unionMan, prop.GetValue(people[0], null), null); 
      } 
     } 
    } 
} 

आपके कहे अनुसार, आप पहले से ही "अवैध" vlaues डिफ़ॉल्ट निर्माता में, सेट कर रहे हैं, ताकि आप इस पाश अंदर उनके बारे में चिंता करने की ज़रूरत नहीं होना चाहिए।

नोट: मेरे उदाहरण में, मैं GetField और GetProperties के संस्करणों है कि एक BindingFlags पैरामीटर नहीं लेते इस्तेमाल किया। ये केवल सार्वजनिक सदस्यों को ही लौटाएंगे।

+0

यह वास्तव में केवल संपत्तियों का एक उप-समूह है। जहां तक ​​अमान्य मान हैं, यह पहले से ही कन्स्ट्रक्टर में किया गया है क्योंकि मैंने अपनी टिप्पणियों में digemAll पलों पहले बताया था। हालांकि, मैं PropertyInfo को स्टोर करने की आपकी सिफारिश की तरह धन्यवाद, धन्यवाद! – Dan