Сохранение класса в файл с разделителями с помощью отражения - PullRequest
0 голосов
/ 01 августа 2009

Я хочу записать имена свойств и соответствующие данные в файл с разделителями, я скопировал некоторый код из файла справки c # objectdumper, и все это работает нормально, но я не понимаю отражения достаточно, чтобы быть уверенным в его использовании. Меня беспокоит то, что неверное значение помещается в неправильный столбец, возможно ли это, например,

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

Ответы [ 4 ]

1 голос
/ 01 августа 2009

Тип t = f! = Ноль? f.FieldType: p.PropertyType;

это встроенный аргумент, если задать f! = Null, тогда f.FieldType else p.PropertyType

можно записать как

Type t;
if (f != null)
    t = f.FieldType;
else 
    t = p.PropertyType; 
0 голосов
/ 02 августа 2009

Просто добавлю некоторые мысли о принятом ответе, в частности, для больших объемов данных:

  • 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();
        }
    }
}
0 голосов
/ 01 августа 2009

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

Приведенный выше код выполняет только GetProperties и GetFields один раз для группы записей - также из-за этого нет необходимости явно сортировать свойства и поля, как это было @ Frederik's предложение.

0 голосов
/ 01 августа 2009

@ 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 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...