Как получить доступ к публичным свойствам List<class> по их порядковому положению

У меня есть открытые свойства класса, и моя строка вывода выводится так:

public static long OutputListToFile(StreamWriter writer, List<DocMetaData> listData)
    {
        StringBuilder md = new StringBuilder();
        int rowcount = 0;
        foreach (var c in listData)
        {
            md.Append(c.Section); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.DocClass); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.Meeting); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.Agency); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.Country); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.Comment); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.Title); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.Folder); md.Append(DocMetaData.SEPARATOR);
            md.Append(c.File); md.Append(DocMetaData.CARRIAGE_RETURN);
            try
            {
                writer.WriteLine(md.ToString());
                rowcount++;
            }
            catch (Exception ex)
            {
                log.Fatal("Error in OutputListToFile:\r\n", ex);
            }
        }
        return rowcount;
    }

В настоящее время операторы md.Append должны быть расположены в указанном выше точном порядке, чтобы соответствовать подпрограмме, записывающей самую первую строку:

private void OutputColumnNamesAsFirstLine(StreamWriter writer)
    {
        StringBuilder md = new StringBuilder();
        PropertyInfo[] columns;
        columns = typeof(DocMetaData).GetProperties(BindingFlags.Public |
                                                      BindingFlags.Instance);
        int i = 0;
        foreach (var columnName in columns)
        {
            i++;
            md.Append(columnName.Name); md.Append(DocMetaData.SEPARATOR);
        }
        writer.WriteLine(md.ToString());
    }

Есть ли способ, чтобы цикл foreach в OutputListToFile мог быть переписан так, чтобы я мог устранить эту зависимость упорядочения. Вот фрагмент класса DocMetaData:

 public class DocMetaData
{
    public const string SEPARATOR = "\t";       // horizontal tab is delimiter
    public const string CARRIAGE_RETURN = "\r";

    public string Section { get; set; }
    public string DocClass { get; set; }
    public string Meeting { get; set; }
    public string Agency { get; set; }
    public string Country { get; set; }
    public string Comment { get; set; }
    public string Title { get; set; }
    public string Folder { get; set; }
    public string File { get; set; }

3 ответа

Решение

Я не уверен, что это то, что вы хотите, но если вы хотите записать содержимое всех свойств в том же порядке, в котором они находятся column, вы можете просто использовать это:

foreach (var c in listData)
{
    foreach (var column in columns)
    {
        md.Append(column.GetValue(c, null));
    }
}

Этот код не использует разделитель и терминатор в качестве исходного кода, но я уверен, что вы легко можете это исправить.

Если вы рассмотрите возможность аннотирования свойств с помощью атрибута, это решение будет работать:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class OrdinalPositionAttribute : Attribute
{
    public int Position { get; private set; }

    public OrdinalPositionAttribute(int position)
    {
        Position = position;
    }
}

public class DocMetaData
{
    public const string SEPARATOR = "\t";       // horizontal tab is delimiter
    public const string CARRIAGE_RETURN = "\r";

    [OrdinalPosition(0)]
    public string Section { get; set; }
    [OrdinalPosition(1)]
    public string DocClass { get; set; }
    [OrdinalPosition(2)]
    public string Meeting { get; set; }
    [OrdinalPosition(3)]
    public string Agency { get; set; }
    [OrdinalPosition(4)]
    public string Country { get; set; }
    [OrdinalPosition(5)]
    public string Comment { get; set; }
    [OrdinalPosition(6)]
    public string Title { get; set; }
    [OrdinalPosition(7)]
    public string Folder { get; set; }
    [OrdinalPosition(8)]
    public string File { get; set; }

    private void OutputColumnNamesAsFirstLine(StreamWriter writer)
    {
        StringBuilder md = new StringBuilder();
        var columns = Utility.GetOrderedMembers(typeof(DocMetaData));

        int i = 0;
        foreach (var columnName in columns)
        {
            i++;
            md.Append(columnName.Name); md.Append(DocMetaData.SEPARATOR);
        }
        writer.WriteLine(md.ToString());
    }
}

public static class Utility
{
    public static IEnumerable<MemberInfo> GetOrderedMembers(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        return CreateGetOrderedMembersEnumerable(type);
    }

    private static IEnumerable<MemberInfo> CreateGetOrderedMembersEnumerable(Type type)
    {
        return from member in type.GetMembers(BindingFlags.Public | BindingFlags.Instance)

               let ordinal = member.GetCustomAttributes(typeof(OrdinalPositionAttribute), true)
                                   .OfType<OrdinalPositionAttribute>().FirstOrDefault()

               where ordinal != null

               orderby ordinal.Position ascending

               select member;
    }
}

Если вы хотите получить значения свойств, вам нужно будет позвонить .Cast<PropertyInfo>() на IEnumerable<MemberInfo> вернулся GetOrderedMembers(),

Рассмотрите возможность использования Dictionary<string, string> для вашего DocMetaData (вместо класса), где первое - это имя поля, а второе - значение. Затем вы можете получить значения, используя массив столбцов.

Другие вопросы по тегам