【问题标题】:Get subclass properties from list of superclass从超类列表中获取子类属性
【发布时间】:2020-12-09 04:06:52
【问题描述】:

我有一个方法可以将List<T> 转换为带有标题行的 TSV 字符串,并且只有具有列表中任何项目的值的行。我之前做的是获取T 的类型并解决它。我现在的问题是,我的列表将不再包含 T 类型的项目,而是从列表类型派生的项目。因此,typeof(T).GetProperties() 将不再起作用,因为这是在查看父类型而不是子类型。

public static string ListToTSVString<T>(List<T> items)
{
    StringBuilder builder = new StringBuilder();
    PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

    // Get only properties that actually have a value in the list.
    var propsDictionary =
        properties.ToDictionary(p => p, p => items.Select(l => p.GetValue(l)).ToArray())
            .Where(pair => pair.Value.Any(o => o != null))
            .ToDictionary(pair => pair.Key, pair => pair.Value);

    // Header row
    builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetCustomAttribute<JsonPropertyAttribute>().PropertyName)));

    // Body of TSV
    foreach (T item in items)
    {
        builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetValue(item))));
    }

    // Remove new line character
    return builder.ToString().TrimEnd();
}

作为我传递的一个例子是:

public class CustomObject
{
    public string GUID { get; set; }
}

public class Referral : CustomObject
{
    public string Name { get; set; }
}

然后我会调用它:List&lt;CustomObject&gt; objects = new List&lt;CustomObject&gt; { new Referral() { Name = "Jimenemex" } }

我试图让 TSV 字符串现在只包含 Referral 类型上声明的属性,而不是 CustomObject 类型。在我只使用不从任何东西继承的对象之前,它工作得很好。

我尝试使用items.GetType().GetGenericArguments()[0],但仍会得到CustomObject 类型。

【问题讨论】:

  • item.GetType() 怎么样?每个项目可能是Referral 或只是一个CustomObject,因此您必须考虑每个项目的类型。

标签: c# reflection type-conversion polymorphism


【解决方案1】:

您可以使用存储在项目集合中的项目类型,而不是使用typeof(T)。请参阅以下代码(使用ConcurrentDictionary 仅缓存属性)

    private static ConcurrentDictionary<Type,  PropertyInfo[]> typeProperties = new ConcurrentDictionary<Type,  PropertyInfo[]>();
    private static PropertyInfo[] GetProperties(Type type)
    {
        if (!typeProperties.ContainsKey(type))
        {
            typeProperties[type] = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        }
        
        return typeProperties[type];
    }
    
    public static string ListToTSVString<T>(List<T> items)
    {
        StringBuilder builder = new StringBuilder();
        PropertyInfo[] properties = GetProperties(items.First().GetType());

        // Get only properties that actually have a value in the list.
        var propsDictionary =
            properties.ToDictionary(p => p, p => items.Select(l => p.GetValue(l)).ToArray())
                .Where(pair => pair.Value.Any(o => o != null))
                .ToDictionary(pair => pair.Key, pair => pair.Value);

        // Header row
        builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetCustomAttribute<JsonPropertyAttribute>().PropertyName)));

        // Body of TSV
        foreach (T item in items)
        {
            builder.AppendLine(string.Join("\t", propsDictionary.Keys.Select(x => x.GetValue(item))));
        }

        // Remove new line character
        return builder.ToString().TrimEnd();
    }

您可以看到这个小提琴 - https://dotnetfiddle.net/HtnFkG 为演示您的场景而创建。

【讨论】:

  • 如果列表包含多种类型,items.First() 将不起作用。如果第一个项目是Referral,它将尝试从所有项目中获取Referral 的属性;如果有任何CustomObject 实例,它将在到达该类型的第一个实例时抛出。必须在循环中检索属性列表。幸运的是,这个解决方案记住了这个结果。
  • 同意,这就是为什么我开始添加字典来捕获属性(通过反射检索),以便如果 OP 需要基于 items 集合中的混合类型,则逻辑可以移动到 foreach 循环中。
猜你喜欢
  • 2013-07-20
  • 1970-01-01
  • 2019-09-16
  • 2021-03-13
  • 1970-01-01
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 2021-03-26
相关资源
最近更新 更多