【问题标题】:How to determine if all properties of type List<T> in an object are null or empty?如何确定对象中 List<T> 类型的所有属性是否为 null 或为空?
【发布时间】:2020-11-19 10:11:49
【问题描述】:

我有一个对象,它包含一些 int/string 属性和一些 List 属性,其中 T 是项目本身中的一些其他类。有没有更简洁的方法来确定只有那些 List 属性是空的还是空的?也许使用 Linq 语句?

我尝试搜索它,但找不到简洁明了的方法。我应该选择反思吗?有人可以提供与此相关的示例吗?

public class A
{
   ..some properties..
   List<ClassA> ListA { get; set; }
   List<ClassB> ListB { get; set; }
   List<ClassC> ListC { get; set; }
   List<ClassD> ListD { get; set; }
   ..some properties..
}

编辑 1: 到目前为止,我已经设法编写了一个干净的代码来检查列表属性是否为空。但是我如何检查它们是否为空。我需要将对象转换为 List,但我不知道 List 的类型

var matchFound = myObject.GetType().GetProperties()
                        .Where(x => x.PropertyType == typeof(List<>))
                        .Select(x => x.GetValue(myObject))
                        .Any(x => x != null);

编辑 2: 我最终使用了这个,一个工作正常的衬里:

var matchFound = myObject.GetType().GetProperties()
                        .Where(x =>(x.GetValue(myObject) as IList)?.Count()>0);

【问题讨论】:

  • List<T>.TrueForAll 方法应该可以。
  • 我的要求是我有一个上述类的对象。有很多列表,所以我不想单独检查 ListA 是否为空,然后 ListB 是否为空等等....是否可以通过 linq 或反射实现??
  • 尝试考虑使用反射递归遍历所有属性 - stackoverflow.com/questions/20554103/…
  • @ChrisRollins:我看不出TrueForAll 在这里有什么用——列表本身只被检查为空(或空引用)。这不需要检查列表的任何元素
  • 请注意,.Where(x =&gt; x.PropertyType == typeof(List&lt;&gt;)) 永远不会找到任何属性。属性不会是List&lt;&gt; 类型,因为那是开放的泛型类型。您需要检测该属性的类型是从List&lt;&gt; 构造的类型。

标签: c#


【解决方案1】:

这就是我要做的。

    /// <summary>
    /// caching a Dyctionary of IList types for faster browsing
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static readonly Dictionary<Type, Type> CachedActualType = new Dictionary<Type, Type>();
    // Get Internal type of IList.
    // When the type is not a list then it will return the same type.
    // if type is List<T> it will return the type of T
    public static Type GetActualType(this Type type)
    {
        if (CachedActualType.ContainsKey(type))
            return CachedActualType[type];

        if (type.GetTypeInfo().IsArray)
            CachedActualType.Add(type, type.GetElementType());
        else if (type.GenericTypeArguments.Any())
            CachedActualType.Add(type, type.GenericTypeArguments.First());// this is almost always find the right type of an IList but if it fail then do the below. dont really remember why this fail sometimes.
        else if (type.FullName?.Contains("List`1") ?? false)
            CachedActualType.Add(type, type.GetRuntimeProperty("Item").PropertyType);
        else
            CachedActualType.Add(type, type);

        return CachedActualType[type];
    }

然后

var matchFound = myObject.GetType().GetProperties()
                        .Where(x => x.PropertyType.GetActualType() != x.PropertyType && 
                              (x.GetValue(myObject) as IList)?.Count()>0);

您实际上可以做得更好,不需要检查类型,只需尝试转换值。 如果类型不是 IList,则该值将始终为 null

var matchFound = myObject.GetType().GetProperties()
                        .Where(x =>(x.GetValue(myObject) as IList)?.Count()>0);

【讨论】:

  • 天哪,我怎么能错过...是的,我可以简单地输入 where 子句...它有效,速度快,维护干净...谢谢..!!
  • 很高兴我能帮上忙 :)
【解决方案2】:

你可以使用反射来满足你的要求,我刚试过。

       class Test
       {

       }
        class UserDetails
        {
            public List<Test> Test1 { get; set; }
            public List<Test> Test2 { get; set; }
            public string firstname { get; set; }
            public string surname { get; set; }
            public string city { get; set; }
            public string state { get; set; }
        }

使用此查询进行搜索,您可以根据需要自定义 where 条件

UserDetails yourObject = new UserDetails();
            yourObject.Test1 = new List<Test> { new Test() };

            var result = typeof(UserDetails).GetProperties()
                .Select(prop => prop)
                .Where(property =>
                {
                    if (property.PropertyType == typeof(List<Test>))
                    {
                        var value = (List<Test>)property.GetValue(yourObject, null);
                        return value == null || value.Count == 0;
                    }

                    return false;
                }).ToList(); // this will return 1 because 1 property has count > 1

如果使用 Templete 则更新

class UserDetails<T>
    {
        public List<T> Test1 { get; set; }
        public List<T> Test2 { get; set; }
        public string firstname { get; set; }
        public string surname { get; set; }
        public string city { get; set; }
        public string state { get; set; }
    }

查询

UserDetails<Test> yourObject = new UserDetails<Test>();
            yourObject.Test1 = new List<Test> { new Test() };

            var result = typeof(UserDetails<Test>).GetProperties()
                .Select(prop => prop)
                .Where(property =>
                {
                    if (property.PropertyType == typeof(List<Test>))
                    {
                        var value = (List<Test>)property.GetValue(yourObject, null);
                        return value == null || value.Count == 0;
                    }

                    return false;
                }).ToList();

【讨论】:

  • 我已经这样做了,但我遇到了问题。如果我参考你的代码,我不知道类 Test ......它可以是任何类,因此问题
  • 你可以改成你的ClassA,ClassB一样
  • 正如我已经告诉过的......这些类在对象中我不知道......没有明确的列表......
  • 你可以发布你的课程来提问
  • 我不能...条款和条件
【解决方案3】:

你需要好好反思一下:

// the type to test
public class TestData
{
    public string A { get; set; }
    public List<string> B { get; set; }
    public List<int> C { get; set; }
}

// an helper class used to generate checking functions
public static class ListTester
{
    public static Func<T, bool> MakeClassChecker<T>()
        where T : class
    {
        var checkFunctions = EnumerateListProperties<T>()
            .Select(MakePropertyChecker<T>)
            .ToList();

        return instance => checkFunctions.All(f => f(instance));
    }

    public static IEnumerable<PropertyInfo> EnumerateListProperties<T>()
    {
        return typeof(T).GetProperties(Instance | Public | NonPublic)
            .Where(prop => IsListClosedType(prop.PropertyType));
    }

    public static Func<T, bool> MakePropertyChecker<T>(PropertyInfo prop)
        where T : class
    {
        var propType = prop.PropertyType;
        var listItemType = propType.GenericTypeArguments[0];

        var listEmptyChecker = (Func<object, bool>) ListCheckerFactoryMethod
            .MakeGenericMethod(listItemType).Invoke(null, new object[0]);

        return instance => instance != null && listEmptyChecker(prop.GetValue(instance));
    }

    private static MethodInfo ListCheckerFactoryMethod
        = typeof(ListTester).GetMethod(nameof(ListCheckerFactory), Static | Public);


    public static Func<object, bool> ListCheckerFactory<T>()
    {
        return list => list == null || ((List<T>) list).Count == 0;
    }

    public static bool IsListClosedType(Type type)
    {
        return type != null &&
                type.IsConstructedGenericType &&
                type.GetGenericTypeDefinition() == typeof(List<>);
    }
}

[Test]
public void TestTemp()
{
    var props = ListTester.EnumerateListProperties<TestData>();
    CollectionAssert.AreEquivalent(props.Select(prop => prop.Name), new[] {"B", "C"});

    var allListsAreNullOrEmpty = ListTester.MakeClassChecker<TestData>();

    Assert.That(allListsAreNullOrEmpty(new TestData()), Is.True);
    Assert.That(allListsAreNullOrEmpty(new TestData() {B = new List<string>()}), Is.True);
    Assert.That(allListsAreNullOrEmpty(new TestData() {B = new List<string>() {"A"}}), Is.False);
}

现在,对于重要的部分:您搜索List&lt;&gt;封闭 泛型类型的属性。 属性的选择在IsListClosedType 中完成。 然后,对于每个属性,我们使用MakePropertyChecker 进行检查。

MakePropertyChecker 的工作是通过MakeGenericMethod 构建ListCheckerFactory 的一个版本 适当的类型。

【讨论】:

    【解决方案4】:

    您要检查所有类型为 List&lt;something&gt; 的属性 这个方法可以解决问题:

    bool IsGenericList(Type t)
    {
        return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
    }
    

    现在您可以修改您的 Linq 查询,使其在至少一个 List 成员不为空或为空时返回

    var matchFound = myObject.GetType().GetProperties()
        .Where(p => IsGenericList(p.PropertyType))
        .Select(p => p.GetValue(myObject) as IEnumerable)
        .Any(list => list != null && list.Cast<object>().Any());//Cast<object> needed to be able to use Linq Any()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-06
      • 1970-01-01
      • 1970-01-01
      • 2016-02-13
      • 1970-01-01
      • 1970-01-01
      • 2012-02-08
      • 1970-01-01
      相关资源
      最近更新 更多