2012-08-02 30 views
6

讓我們先從一類定義爲例子來說:聯盟基於其成員的平等兩個物體

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

現在讓我們假設我有一個包含3個對象List<Person>稱爲people

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

我正在尋找一些方法來檢索以下單Person對象:

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

其中在所有對象中相同的字段保留其值,而具有多個值的字段被替換爲某個無效的值。

我首先想到的組成如下:

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

好,你的第二個想法應該工作,但不是顯示的代碼!順便說一句,你應該有一個Person的默認構造函數與你的「無效」值。 – 2012-08-02 20:27:32

回答

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 2012-08-02 20:57:20

+0

+1非常elegeant。你只是忘了在參數聲明中加入'this'這個詞。 – 2012-08-02 21:42:42

+0

@jmh_gr謝謝,修正 – 2012-08-02 21:50:49

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); 
} 

一對夫婦的意見:

  • 類成員應該被定義爲屬性而不是公共成員 和都得到並且set訪問器必須公開
  • 默認構造函數應該定義「無效」值(正如@RaphaëlAlthaus建議的那樣)

所以,類人是這樣的:

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.GetValue()'和'PropertyInfo.SetValue()'來使用'PropertyInfo.GetGetMethod()'和'PropertyInfo.GetSetMethod()'?如果不是的話,我認爲後者略少一些。 – 2012-08-02 20:48:30

+0

@JonSenchyna:只要我忘了那些shourtcuts:P謝謝你的提示;) – digEmAll 2012-08-02 20:50:47

+0

是啊,這是一個簡單的例子類我在SO編輯扔在一起,真正的目標確實有一個構造函數,每個成員設置爲它的「無效「狀態。就屬性和成員而言,我正在使用的真實對象是在外部程序集中定義的,由於依賴性問題,我無法更改。 – Dan 2012-08-03 00:04:04

1

更新:既然你沒有在Person類控制和狀態在公共領域的定義,而不是性質,我已經更新解決方案來解決這個問題。

我會推薦使用反射。您希望提前獲取FieldInfo(或PropertyInfo)對象,而不是在LINQ查詢中獲取每個條目。你可以通過使用Type.GetFieldType.GetProperty來獲得它們。一旦你擁有了這些,你可以簡單地使用FieldInfo/PropertyInfo.GetValueFieldInfo/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在默認的構造函數,所以你不應該擔心他們這個循環中。

注:在我的例子,我以前不採取BindingFlags參數GetFieldGetProperties版本。這些只會返回公衆成員。

+0

它確實只是屬性的一個子集。就無效值而言,這已經在構造函數中完成了,正如我在digEmAll之前的評論中指出的那樣。我喜歡你的建議來存儲PropertyInfo,謝謝! – Dan 2012-08-03 00:08:25