从集合

时间:2016-11-24 12:38:25

标签: c# .net generics reflection

我有以下代码。第一行应该是逗号分隔的标题字符串,剩下的应该是我的集合的内容,可以写入逗号分隔文件。我如何获得价值?

public static List<string> ToListOfStrings<T>(this List<T> items, string sep)
{
    var NewList = new List<string>();
    //string tempHeaderString = "";

    PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

    string s = string.Empty;
    foreach (var prop in props)
    {
        s += prop.Name + sep;

    }
    NewList.Add(s + System.Environment.NewLine);
    foreach (var item in items)
    {
        string s1 = string.Empty;
        var values = new object[props.Length];
        for (var i = 0; i < props.Length; i++)
        {
            s1 += (props[i].GetValue(item, null)).ToString() + sep;
        }

        NewList.Add(s1 + System.Environment.NewLine);
    }

    return NewList;
}

我的目标对象T只有字符串作为成员属性。

最佳

3 个答案:

答案 0 :(得分:1)

假设您有以下对象:

public class User
{
    public string Name { get; set; }
    public string Password { get; set; }
}

然后说你有以下代码:

User user = new User { Name = "Pete", Password = "Secret" };
Console.WriteLine(user.ToString());

输出MyNamespace.User, MyAssembly。您将期望输出什么? PeteSecret

框架无法决定 你,所以你必须提出自己的解决方案。如果您拥有该类,则可以添加自己的ToString()实现:

public override string ToString()
{
    return Name;
}

但是,如果您不拥有代码,则必须单独为每种类型编写转换器,如果您想要控制哪个属性将为您的CSV提供值。

答案 1 :(得分:0)

据我在评论中理解,只有代码中的问题是属性值的字符串表示。我已经简化了一些代码并创建了一个特殊的方法,您可以在其中放置逻辑以将值转换为字符串,或者如果它是属性中的类型,则可以为类型重载ToString()方法。

    public static IEnumerable<string> ToListOfStrings<T>(this List<T> items, string sep)
    {
        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        yield return properties.Select(p => p.Name).CreateRow(sep);

        foreach (var item in items)
        {
            yield return properties.Select(p => GetString(p.GetValue(item, null))).CreateRow(sep);
        }
    }

    private static string CreateRow(this IEnumerable<string> values, string separator)
    {
        return string.Join(separator, values) + System.Environment.NewLine;
    }

    public static string GetString(object obj)
    {
        //custom logic to create string from values
        return obj.ToString();
    }

<强>更新

根据您的评论,您还希望显示嵌套对象的属性。这可以这样做:

    public static IEnumerable<string> ToListOfStrings<T>(this IEnumerable<T> items, string sep)
    {
        var properties = typeof(T).GetInstanceProperties();
        yield return properties.Select(p => p.Name).CreateRow(sep);

        foreach (var item in items)
        {
            yield return properties.GetValues(item, sep).CreateRow(sep);
        }
    }

    private static string CreateRow(this IEnumerable<string> values, string separator)
    {
        return values.JoinStrings(separator) + System.Environment.NewLine;
    }

    private static string JoinStrings(this IEnumerable<string> values, string separator)
    {
        return string.Join(separator, values);
    }

    public static PropertyInfo[] GetInstanceProperties(this Type type)
    {
        return type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
    }

    private static IEnumerable<string> GetValues(this IEnumerable<PropertyInfo> properties, object obj, string separator)
    {
        return properties.Select(p => GetString(p.GetValue(obj, null), separator));
    }

    public static string GetString(object obj, string separator)
    {
        var type = obj.GetType();
        if (type.IsPrimitive || type.IsEnum || type == typeof(string))
            return obj.ToString();

        var result = obj.GetType().GetInstanceProperties()
                        .GetValues(obj, separator)
                        .JoinStrings(separator);

        return string.Format("({0})", result);
    }

我已将括号中的嵌套对象包装在一起,以显示值的层次结构并保持标题行的一致性。

答案 2 :(得分:0)

我已运行您的代码,它适用于T的此类属性,这些属性属于基本类型,例如stringint

class Test
{
    public string FString { get; set; }
    public int FInt { get; set; }
}

/* ... */

List<Test> x = new List<Test>
{
    new Test {FString = "A", FInt = 10}, new Test {FString = "B", FInt = 20}
};

输出:

FString;FInt;
A;10;
B;20;

因此代码本身没有问题。

我从您的评论中了解到,有一些自定义类用作T中字段的类型(例如LINQPad.User.Header)。正如其他人所提到的,每个类通过重载ToString()单独负责将自己表示为字符串。只有一个类可以理解如何将其内容转换为有意义的(虽然通常是剥离的)字符串表示。默认是告诉类的名称。这显然不是你所期望的。

您希望以某种方式告诉编译器 您希望将LINQPad.User.Header表示为字符串。最基本的选项 - 如果您是此类的创建者,则将默认ToString()替换为自定义实现。另一个解决方案是子类LINQPad.User.Header并覆盖子类中的ToString()。仍然不确定这是否可以在您的方案中轻松应用。另一种选择是在CSV创建期间识别出您正在处理的对象 a LINQPad.User.Header并相应地写出来(通过使用其公共字段,即。)。

使用Convert.ToString(props[i].GetValue(item,null))不是一个选项,因为Convert也不知道 如何将随机类表示为字符串。