【问题标题】:Invoke a generic method for properties which are known at runtime为运行时已知的属性调用通用方法
【发布时间】:2019-03-11 10:28:01
【问题描述】:
public partial class ReturnHeader
{
        public int ReturnHeaderId { get; set; }
        public int CustomerId { get; set; }
        public string InvoiceNo { get; set; }
        public virtual Customer Customer { get; set; }
        public virtual ICollection<ReturnDetail> ReturnDetails { get; set; }
}

public void TraverseThroughClass<T> (T entity) where T : class
{
 try
 {
  var type = typeof(T);
  PropertyInfo[] props = type.GetProperties();
  foreach (PropertyInfo prop in props)
  {
   if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
  {

此检查确定属性是否为列表。

问题 1:如果为真,则将其转换为列表或任何集合。 我想要做的是,如果属性是一个集合,则对它进行转换,并为转换集合中的每个项目调用TraverseThroughClass

    /* Error casting into any collection*/
    //var propertiesValues = prop.GetValue(entity) as IList;
    var listType = GetCollectionItemType(prop.PropertyType);
    foreach (var listItem in propertiesValues)
    {

问题 2:对于集合中的每个项目调用 TraverseThroughClass(T 实体)

    }
  }
  else
  {
   Console.WriteLine("Prop Name : " + prop.Name + " Prop Value : " 
            + prop.GetValue(entity, null));
  }
}
 }
 catch (Exception e)
 {
  throw;
 }
}     

 public static Type GetCollectionItemType(Type collectionType)
 {
  var types = collectionType.GetInterfaces()
            .Where(x => x.IsGenericType
                && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            .ToArray();
        return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
    }

【问题讨论】:

  • 一个string 是一个IEnumerable&lt;Char&gt;。你觉得这很奇怪吗?
  • 我不像你@Damien_The_Unbeliever 那样是专家。谢谢你顺便告诉我。我会更新问题。
  • comparedEntityHashSets.Add(entity.GetHashCode()); - 闻起来很香。允许不同的实体具有相同的哈希码。为什么不直接将 entity 添加到 HashSet 中?
  • 我会删除它。忽略那部分。
  • 我实际上并不清楚这里的问题是什么。例如,我在您的问题中没有看到一个问号。

标签: c# asp.net-mvc entity-framework generics reflection


【解决方案1】:

您似乎正在尝试将ICollection 转换为IList,这可能有效,也可能无效。运行时属性的值可能是未实现 IListICollection

如果此属性的运行时值实现IList,它将起作用,但如果不是,它将失败。

public virtual ICollection<ReturnDetail> ReturnDetails { get; set; }

由于您正在检查属性类型是否可从 IEnumerable 分配,所以我们保持一致并在任何地方使用它。

另一个混淆是TraverseThroughClass 采用entity 参数,但在上面的语句中,您使用this 来调用属性。您的意思是调用entity 上的属性吗?那会更有意义。否则,它正在切换——方法的一部分调用entity 的属性,一部分调用this 的属性,这是调用该方法的类实例。

为了澄清这一点,我将TraverseThroughClass 移动到它自己的静态类中,并对entity 参数执行所有操作。我还删除了GetCollectionItemType。它被调用了,但调用的结果从未使用过,所以看起来我们不需要它。

这是您的方法的修改版本,已移至其自己的类中。 (请注意,我并没有解决这个尝试做的更大的图景,只是试图解决你问题中的问题。)

public static class Traversal
{
    public static void TraverseThroughClass<T>(T entity) where T : class
    {
        var type = typeof(T);
        PropertyInfo[] props = type.GetProperties();
        foreach (PropertyInfo prop in props)
        {
            if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
            {
                var propertiesValues = prop.GetValue(entity);

                // What if the property value is null?
                if (propertiesValues == null) continue;
                var collection = propertiesValues as IEnumerable;
                foreach (var listItem in collection)
                {
                    // I don't know what you want to do with these. 
                    // I'm just confirming that we're able to inspect them.
                    Debug.WriteLine("Success, we're iterating over the items!!"); 
                }
            }
            else
            {
                Debug.WriteLine("Prop Name : " + prop.Name + " Prop Value : "
                                  + prop.GetValue(entity, null));
            }
        }
    }
}

这仍然留下了您的方法将遍历 InvoiceNo 的细节,因为它实现了 IEnumerable(它是字符的集合。)如果您不想这样做,您可以专门排除字符串或更具体地说明什么您确实想要遍历的类型。

此时我可以执行这段代码了:

var returnHeader = new ReturnHeader
{
    ReturnDetails = new List<ReturnDetail>(
        new ReturnDetail[] { new ReturnDetail(), new ReturnDetail() }),
    InvoiceNo = "Invoice!"
};
Traversal.TraverseThroughClass(returnHeader);

...遍历将遍历InvoiceNo 中的字符和ReturnDetails 中的项目。

【讨论】:

  • 我明天在办公室测试一下。到目前为止,您的 IEnumerable 事情是有道理的。但另一个问题仍然存在,我也需要为每个项目调用TraverseThroughClass。我不知道我的类比是否正确,但我想我可以说 DFS 的通用实现。
  • 编写使用反射来遍历具有嵌套集合的复杂对象的无类型代码是一种痛苦。有一天你会需要一本字典或其他东西,然后你也必须考虑到这一点。您可能会发现为单个类编写方法要容易得多。您必须为属性和集合编写特定的代码,但除非您处理大量的类型,否则它可能会更快更容易。
  • 我感到痛苦,但这是一种需要,因为它必须在太多地方实施。所以我想到了使用泛型。如果它变得太乱,我会转向简单的方法,但我想尝试一下,就好像它成功一样,它会节省我很多精力。
  • 我编辑了我的评论,因为我说的是“泛型”,但并不是指“泛型”。我正在考虑更多尝试解释每种属性的代码。
猜你喜欢
  • 1970-01-01
  • 2019-01-20
  • 1970-01-01
  • 2013-06-12
  • 2016-09-12
  • 2020-05-21
  • 2013-05-17
  • 1970-01-01
  • 2018-10-16
相关资源
最近更新 更多