2009-08-01 19 views
0

我想將屬性名和匹配數據寫入分隔文件,我從c#objectdumper幫助文件複製了一些代碼,它似乎都可以正常工作,但我不理解反射足以有信心使用它。我擔心的是一個錯誤的值被放在不正確的列中,是否有可能發生這種情況,例如,使用反射將類保存到delim文件

Field1,Field2 
Val1,Val2 
Val1,Val2 
Val2,Val1 << Could this ever happen ? 

此外,這段代碼是什麼意思?下面

f != null ? f.GetValue(this) : p.GetValue(this, null) 

代碼:

public string returnRec(bool header, string delim) 
{ 
    string returnString = ""; 
    bool propWritten = false; 
    MemberInfo[] members = this.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance); 
    foreach (MemberInfo m in members) 
    { 
     FieldInfo f = m as FieldInfo; 
     PropertyInfo p = m as PropertyInfo; 
     if (f != null || p != null) 
     { 
      if (propWritten) 
      { 
       returnString += delim; 
      } 
      else 
      { 
       propWritten = true; 
      } 
      if (header) 
       returnString += m.Name; 
      else 
      { 
       Type t = f != null ? f.FieldType : p.PropertyType; 
       if (t.IsValueType || t == typeof(string)) 
       { 
        returnString += f != null ? f.GetValue(this) : p.GetValue(this, null); 
       } 
      } 
     } 
    } 
    return returnString; 
} 

回答

0

@astander和@Frederik已經基本上回答了你專門配音的問題和顧慮,但我想建議以更有效的方式做事。根據您希望寫入文件的對象實例的數量,您提交的方法可能最終效率非常低。這是因爲你在每次迭代時都會通過反思收集類型和價值信息,這是不必要的。

您要查找的內容是查找類型信息一次,然後僅使用反射來獲取屬性和字段的值,例如, (.NET 3.5),

public static IEnumerable<string> ReturnRecs(IEnumerable items, bool returnHeader, string delimiter) 
{ 
    bool haveFoundMembers = false; 
    bool haveOutputHeader = false; 
    PropertyInfo[] properties = null; 
    FieldInfo[] fields = null; 
    foreach (var item in items) 
    { 
     if (!haveFoundMembers) 
     { 
      Type type = item.GetType(); 
      properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) 
       .Where(pi => pi.PropertyType.IsValueType || pi.PropertyType == typeof (string)).ToArray(); 
      fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance) 
       .Where(fi => fi.FieldType.IsValueType || fi.FieldType == typeof(string)).ToArray(); 
      haveFoundMembers = true; 
     } 
     if (!haveOutputHeader) 
     { 
      yield return String.Join(delimiter, properties.Select(pi => pi.Name) 
            .Concat(fields.Select(pi => pi.Name)).ToArray()); 
      haveOutputHeader = true; 
     } 
     yield return String.Join(delimiter, 
           properties.Select(pi => pi.GetValue(item, null).ToString()) 
            .Concat(fields.Select(fi => fi.GetValue(item).ToString())).ToArray()); 
    } 

上面的代碼永遠只能執行GetPropertiesGetFields組記錄 - 兼一次,正因爲如此,沒有必要的屬性和領域是明確排序@弗雷德裏克的建議。

+0

謝謝,雖然它可能只會節省幾秒鐘,我會使用它。 – 2009-08-01 22:22:15

1

類型T = F!= NULL? f.FieldType:p.PropertyType;

這是一個內聯如果問爲f!= null,則f.FieldType其他p.PropertyType

可以作爲

Type t; 
if (f != null) 
    t = f.FieldType; 
else 
    t = p.PropertyType; 
+0

+1:很好的答案,所以我把它出礦。 – 2009-08-01 08:52:04

+0

謝謝,不記得是什麼被稱爲甚至查找它。 – 2009-08-01 22:20:51

0

@astander已經給你一個答案的Type t = f != null ? f.FieldType : p.PropertyType;寫問題,所以我會離開那一個。關於將值列入正確的列,我不知道反射保證以特定的順序列出類型的成員,但是您可以通過在使用它之前對列表進行排序來保證它(使用Linq):

MemberInfo[] members = typeof(Item).GetMembers(BindingFlags.Public | BindingFlags.Instance); 
IEnumerable<MemberInfo> sortedMembers = members.OrderBy(m => m.Name); 
foreach (MemberInfo member in sortedMembers) 
{ 
    // write info to file 
} 

或者如果你喜歡一個非LINQ的方法(與.NET框架2.0工程):

MemberInfo[] members = typeof(Item).GetMembers(BindingFlags.Public | BindingFlags.Instance); 
Array.Sort(members, delegate(MemberInfo x, MemberInfo y){ 
    return x.Name.CompareTo(y.Name); 
}); 

foreach (MemberInfo member in members) 
{ 
    // write info to file 
} 
+0

正在尋找GetMembers返回順序的「證明」,但沒有發現任何可以說明的事情。所以我同意上面提到的方法 – 2009-08-01 08:50:57

+0

謝謝你,讓我的腦海休息。 – 2009-08-01 22:21:27

0

我想補充一些想法重新接受的答案,特別是對於大數據量:

  • PropertyInfo等可能不必要地慢;有辦法避免這種情況,例如使用HyperDescriptor或其他動態代碼
  • 而不是建立大量中間串的,也可能是更有效的直接寫入輸出到TextWriter

作爲該採用微調的版本這些方法,見下文;請注意,我沒有啓用這個例子HyperDescriptor,但也只是:

HyperTypeDescriptionProvider.Add(typeof(YourType)); 

反正...

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.IO; 
static class Program { 
    static void Main() { // just some test data... 
     var data = new[] { new { Foo = "abc", Bar = 123 }, new { Foo = "def", Bar = 456 } }; 
     Write(data, Console.Out, true, "|"); 
    } 
    public static void Write<T>(IEnumerable<T> items, TextWriter output, bool writeHeaders, string delimiter) { 
     PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); 
     foreach (T item in items) { 
      bool firstCol = true; 
      if (writeHeaders) {     
       foreach (PropertyDescriptor prop in properties) { 
        if (firstCol) { 
         firstCol = false; 
        } else { 
         output.Write(delimiter); 
        } 
        output.Write(prop.Name);      
       } 
       output.WriteLine(); 
       writeHeaders = false; 
       firstCol = true; 
      } 
      foreach (PropertyDescriptor prop in properties) { 
       if (firstCol) { 
        firstCol = false; 
       } else { 
        output.Write(delimiter); 
       } 
       output.Write(prop.Converter.ConvertToString(prop.GetValue(item))); 
      } 
      output.WriteLine(); 
     } 
    } 
}