2009-08-01 5 views
0

Ich möchte die Eigenschaftsnamen und die übereinstimmenden Daten zu einer Datei mit Trennzeichen schreiben, ich habe Code aus der C# -Objektdumper-Hilfedatei kopiert und alles scheint OK zu funktionieren, aber ich verstehe nicht Reflexion genug, um sicher zu sein, es zu benutzen. Was mich beunruhigt, ist ein falscher Wert, der in die falsche Spalte gesetzt wird, ist es möglich, dass dies z.B.Speichern einer Klasse in eine Delim-Datei mit Reflektion

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

Auch was bedeutet dieses Stück Code?

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

-Code unten:

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

Antwort

0

@astander und @Frederik beantwortet haben im wesentlichen die Fragen und Bedenken, die Sie speziell geäußert, aber ich möchte zu empfehlen, Dinge etwas effizienter zu machen. Abhängig von der Anzahl der Objektinstanzen, die Sie in Ihre Datei schreiben möchten, ist die von Ihnen vorgeschlagene Methode möglicherweise sehr ineffizient. Das liegt daran, dass Sie Typ- und Wertinformationen durch Reflexion bei jeder Iteration nachlesen, was unnötig ist.

Nach was Sie suchen, ist etwas, das Typinformation einmal nachschaut und dann nur Reflexion verwendet, um den Wert von Eigenschaften und Feldern, z. (.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()); 
    } 

Der obige Code immer nur ein GetProperties und GetFields einmal pro Gruppe von Datensätzen --also, weil dieser führt, gibt es keine Notwendigkeit, explizit die Eigenschaften und Felder zu sortieren, wie es @ Frederiks Vorschlag.

+0

Danke, obwohl es wahrscheinlich nur Sekunden sparen wird, werde ich das verwenden. –

1

Typ t = f = null!? f.FieldType: p.PropertyType;

dies ist ein Inline-wenn, ist zu fragen f! = Null dann f.FieldType sonst p.PropertyType

kann geschrieben werden als

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

+1: gute Antwort, so ließ ich es aus mir. –

+0

Danke, ich konnte mich nicht erinnern, wie es genannt wurde, um es nachzuschlagen. –

0

@astander bereits Sie eine Antwort auf die Type t = f != null ? f.FieldType : p.PropertyType; gegeben hat Frage, also werde ich das auslassen. In Bezug auf die Werte in die richtigen Spalten weiß ich nicht, ob die Reflektion garantiert, die Elemente eines Typs in einer bestimmten Reihenfolge aufzulisten, aber Sie können es garantieren, indem Sie die Liste vor der Verwendung sortieren (mit 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 
} 

Oder wenn Sie einen nicht-Linq Ansatz bevorzugen (funktioniert mit .NET Framework 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

War auf der Suche nach "Beweis" der Reihenfolge, in der die GetMembers zurückkehren, fand aber nichts, was aussagte. So stimme ich dem oben genannten Ansatz zu –

+0

Danke dafür, beruhigt mich. –

0

Nur ein paar Gedanken Wieder die akzeptierte Antwort, insbesondere für große Datenmengen hinzufügen:

  • PropertyInfo etc unnötig langsam sein kann; es gibt Möglichkeiten, dies zu vermeiden, zum Beispiel HyperDescriptor oder anderen dynamischen Code
  • anstatt viele Zwischen Saiten bauen, kann es effizienter sein Ausgang an einen TextWriter

Als gezwickt Version direkt zu schreiben, nimmt diese Ansätze, siehe unten; beachten Sie, dass ich nicht HyperDescriptor in diesem Beispiel aktiviert haben, aber das ist nur:

HyperTypeDescriptionProvider.Add(typeof(YourType)); 

Wie dem auch sei ...

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(); 
     } 
    } 
} 
Verwandte Themen