【问题标题】:Set Object null if all its properties are null in C#如果在 C# 中它的所有属性都为 null,则将 Object 设置为 null
【发布时间】:2018-12-12 14:24:08
【问题描述】:

我想编写一个函数来转换对象的每个属性和子属性。如果一个属性的所有属性都为空,那么我将该属性设置为空。我将举一个例子来解释。

例如,如果TeacherNameTeacherSurname 都为空,那么我想将Teacher 设置为空。那么,如果ExamMarkExamNameTeacher 为空,那么Exam 将为空。

你可以从这个链接Json Version看到像我的问题的json版本

public class Student
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public Address Address { get; set; }
    public Exam Exam { get; set; }
}

public class Exam
{
    public string ExamMark { get; set; }
    public string ExamName { get; set; }
    public Teacher Teacher { get; set; }
}

public class Teacher
{
    public string TeacherName { get; set; }
    public string TeacherSurname { get; set; }
}

public class Address
{
    public string Country { get; set; }
    public string City { get; set; }

}

我写了这个方法。但这只是第一步。但是我需要对子类进行递归。如何将此方法转换为递归?

public static object ConvertToNull(object obj)
{
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();

    var mainClassProperties = properties.Where(p => p.PropertyType.Assembly == objType.Assembly);

    foreach (var mainClassProperty in mainClassProperties)
    {
       object propValue = mainClassProperty.GetValue(obj, null);
       var classAllProperties = propValue.GetType().GetProperties();

       if (propValue.GetType().GetProperties().All(propertyInfo => propertyInfo.GetValue(propValue) == null))
       {
           mainClassProperty.SetValue(obj, null);
       }
   }
    return obj;
}

【问题讨论】:

  • 这真的不是一个伟大的设计。您能否提供更多关于如何创建这些对象以及为什么允许在无效状态下创建它们的背景信息?
  • 我的课只是讲述我的故事的例子。在我的真实场景中,一个来自具有 300 属性的外部 Web 服务的对象。但是,很多子值都是空的,就像我的问题一样。所以,我想将它转换为基本对象。 @KirkLarkin

标签: c# asp.net asp.net-mvc


【解决方案1】:

使用泛型和反射。

    public T ConvertToNull<T>(T model) where T : class
    {
        if (model == null) return null;
        Type type = model.GetType();
        PropertyInfo[] properties =  type.GetProperties();

        var valueTypes = properties.Where(p => p.PropertyType.Assembly != type.Assembly);
        var nonValueTypes = properties.Where(p => p.PropertyType.Assembly == type.Assembly);

        foreach (var nonValueType in nonValueTypes)
            nonValueType.SetValue(model, ConvertToNull(nonValueType.GetValue(model)));

        if (valueTypes.All(z => z.GetValue(model) == null) && nonValueTypes.All(z => z.GetValue(model) == null))
            return null;
        else
            return model;
    }

这里可以调用

        List<Student> students = new List<Student>();
        Student student = new Student() { Name = "StudentName", Surname = "StudentSurname", Address = new Address() { City = "City", Country = "Country" }, Exam = new Exam() { ExamMark = "ExamMark", ExamName = "ExamName", Teacher = new Teacher() { TeacherName = "TeacherName", TeacherSurname = "TeacherSurname" } } };

        Student student2 = new Student() { Name = "StudentName", Surname = "StudentSurname", Address = new Address(), Exam = new Exam() { ExamMark = "ExamMark", ExamName = "ExamName", Teacher = new Teacher() } };

        students.Add(student);
        students.Add(student2);

        List<Student> results = new List<Student>();
        foreach (var item in students)
        {
            var result = ConvertToNull(item);
            results.Add(result);
        }

【讨论】:

  • 谢谢@UmairAnwaar。 npo 的回答和你的回答都非常有效。
【解决方案2】:

看看这个 GetValueOrNull 应该可以工作并做你需要的事情,没有用所有可能的用例进行测试,但如果不能在所有情况下都工作,它可以稍微调整一下

public static bool IsSimpleType(Type type)
{
    return
        type.IsPrimitive ||
        new Type[] {
    typeof(Enum),
    typeof(String),
    typeof(Decimal),
    typeof(DateTime),
    typeof(DateTimeOffset),
    typeof(TimeSpan),
    typeof(Guid)
        }.Contains(type) ||
        Convert.GetTypeCode(type) != TypeCode.Object ||
        (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
        ;
}
public object GetValueOrNull(object obj)
{
    if (obj == null) return null;
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    var simpleTypes = properties.Where(t => IsSimpleType(t.PropertyType));

    var nonValueTypes = properties.Where(p => !simpleTypes.Contains(p));
    foreach (var child in nonValueTypes)
    {
        child.SetValue(obj, GetValueOrNull(child.GetValue(obj)));
    }
    return simpleTypes.All(z => z.GetValue(obj) == null) && nonValueTypes.All(z => z.GetValue(obj) == null) ? null : obj;
}

如下调用

var student = new Student { Address = new Address { }, Exam = new Exam { Teacher = new Teacher() } };
            var test = GetValueOrNull(student);

希望对你有帮助:)

【讨论】:

  • 但是,如果您将部分代码 var simpleTypes = properties.Where(t =&gt; IsSimpleType(t.PropertyType)); var nonValueTypes = properties.Where(p =&gt; !simpleTypes.Contains(p)); 更改为此代码 var simpleTypes = properties.Where(p =&gt; p.PropertyType.Assembly != objType.Assembly); var nonValueTypes = properties.Where(p =&gt; p.PropertyType.Assembly == objType.Assembly); ,那么您可以删除 IsSimpleType 方法。因为你不需要那种方法。如果我是对的,你可以编辑你的答案吗? @user5195490
  • 非常感谢。您的解决方案完美运行。但是UmairAnwaar 的解决方案更短。所以,我标记为答案。 @npo
  • @HasanOzdemir 是的,看起来更短,唯一缺少的是检查其原始值的函数。但是我认为这不会一直有效,不属于您的程序集的类会发生什么让它们来自某个 nuget 包,并且您在类中使用了一些模型。这不会是与父对象相同的程序集吗?
  • 再次感谢。是的。在这种情况下,UmairAnwaar 的答案将运行不正确。我认为,将这个答案标记为正确答案会更正确。 @npo
【解决方案3】:

你所要求的实际上是一种反模式。这样做的目的是在代码中传播对大量空值检查的要求。您不仅要检查所有属性是否为空,还必须在要使用对象时检查它是否为空。反射也很慢,如果您经常这样做,您的应用程序就会陷入困境。

您应该查看Null Object Pattern。它基本上做你想做的事,并且你删除了所有那些讨厌的空检查:

public class Student
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public Address Address { get; set; }
    public Exam Exam { get; set; }
    public static Student NullStudent { get; } = new Student
    {
        Name = null,
        Surname = null,
        Address = Address.NullAddress,
        Exam = Exam.NullExam,
    }
}

您可以对代码中以这种方式运行的每个对象执行此操作(您可以看到我也在嵌套的 Address 和 Exam 类型上执行此操作),然后不执行此操作:

if (Student.EverythingIsNull) { Student = null }
if (Student is null) { //do null stuff }

你可以这样做:

if (item == Student.NullStudent) { //do null stuff }

这会导致代码更清晰,您的意图更加突出,并且您可以在每个对象中具体定义什么构成 null。

【讨论】:

  • 谢谢。但是,在您的解决方案中,您创建的对象。但在我的情况下,对象不是由我创建的。因为对象来自外部 Web 服务。
  • 在提供的示例中,item 是来自 Web 服务的项目,Student.NullStudent 是您创建的用于比较的项目。基本上,您构建对象的“已知空状态”,然后将传入的对象与它进行比较。
  • @HasanOzdemir Student.NullStudent 不是您的 Web 服务创建的对象。这是您定义的可比较对象。你基本上说的是“这是这个对象的空版本的样子。如果我从我的网络服务获得的对象具有NullStudent 的所有值(在这种情况下都是空值),那么它本质上是空值。”如果“我没有创建对象”是指“这个类位于我无法触及的单独源代码中”,那么您可以在某处将NullStudent 定义为静态成员。
  • 感谢您的回复。我现在在@TheHeadmaster 发表评论后了解您的回答。但这对于我的真实情况是不可能的。因为在我的场景中,会有超过 300 个属性。还有很多...的孩子的孩子的孩子,这只是一个例子。会有20节这样的课。因此,这意味着成千上万的代码行。 user5195490 的答案是我在寻找什么。再次感谢。
  • @HasanOzdemir 我明白,很高兴能提供帮助。如果那是您的真实世界场景,那么这听起来像是一场噩梦,这些物体听起来像是在做太多事情。如果您确实对在公司中进行更改有任何影响,我会敦促您这样做。祝你好运。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-12
  • 1970-01-01
  • 1970-01-01
  • 2012-01-25
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多