简体   繁体   中英

Set Object null if all its properties are null in C#

I want to write a function which it turns every properies and child properties of an object. And if all properties of one property are null, then I will set that property as null. I will explain with one example.

For example, if both TeacherName and TeacherSurname are null then I want to set Teacher to null. Then, if ExamMark and ExamName and Teacher are null, then Exam will be null.

You can see json version of like my question from this link Json Version

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; }

}

I wrote this method. But this doing only first step. But I need recursive for child class. How can I convert this method to recursive?

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;
}

using Generics and Reflection.

    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;
    }

Here you can call it

        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);
        }

What you're asking for is actually an anti-pattern. What this does is it propagates the requirement for tons of null checks in your code. Not only do you have to check if all properties are null, you also have to check if your object is null when you want to use it. Also reflection is very slow and if you're doing this often, you'll bog down your application.

You should look into the Null Object Pattern . It basically does what you want it to, and you remove all those pesky null checks:

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,
    }
}

You can do this for each object in your code that acts this way (you can see that I did it on your nested Address and Exam types as well) and then instead of doing this:

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

You can do this:

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

This leads to clearer code, your intent stands out more, and you can specifically define in each object what constitutes as null.

Have a look at this The GetValueOrNull should work and do what you need, not tested with all possible use cases but it oculd be tweaked a little if doesn't work in all cases

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;
}

call it as follows

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

hope it helps :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM