简体   繁体   中英

Want to invoke a base class using a generic with 'this' of child class

So my problem is that I want to remove nulls for all my strings in complex objects like POCOs and DTOs. I can do this but the method I am doing it seems like it could be better. So I figured someone on Stack Overflow had a better way.

Base Class:

public class PropertyEmptySetter<T> where T : class
{
    T obj { get; set; }

    public PropertyEmptySetter(T myObj)
    {
        obj = myObj;
        UpdateStringProperties();
    }

    public PropertyEmptySetter()
    {
    }

    public void UpdateStringProperties()
    {
        foreach (var prop in obj.GetType().GetProperties().ToList())
        {
            if (prop.PropertyType == (typeof(string)))
            {
                if(prop.GetValue(obj) == null)
                    prop.SetValue(obj, String.Empty);
            }
        }
    }
}

Child Class (POCO Object):

public class POCO : PropertyEmptySetter<POCO>
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Purpose { get; set; }

    public POCO()
    {
       var setter = new PropertyEmptySetter<POCO>(this);
    }
}

Testing it works:

private static string _s = "";

static void Main(string[] args)
{

    var item = new POCO {Id = 1, Name = "Brett", Description = "Me"};

    var props = item.GetType().GetProperties();
    foreach (var prop in props)
    {
        _s += "Type: " + prop.Name + "\tValue:" + (prop.GetValue(item) ?? "NULL") + Environment.NewLine;

    }

    Console.WriteLine(_s);
    Console.ReadLine();
}

So the above works but I was hoping to shorten it to something like the child constructor stating:

 public POCO() : base(this)

Ehh, ehhh. Visual Studio does not like this and states "Cannot use 'this' in member initializer"

Or have the parameterless constructor in the base do something like

public PropertyEmptySetter()
{
    obj = (child)  // Yes I know this does not exist
        UpdateStringProperties();
}

I basically was hoping to scaffold up all the logic so that the inheritance just knows: "Oh you want this object you are creating RIGHT NOW, gotcha!" It seems that I have to do this in the scope of the constructor and cannot get around that. But I may just be ignorant of an easier way. I do not want to this when instantiating the object outside of it, I want this so it just scaffolds up when the object is created inside itself and the base class. Is this even possible or is the method I am showing the easiest way?

The simplest solution is just to make PropertyEmptySetter non-generic as it doesn't need to be. It just works on this and every class the inherits from it passes this simply by being PropertyEmptySetter :

public class PropertyEmptySetter
{
    public PropertyEmptySetter()
    {
        UpdateStringProperties();
    }

    public void UpdateStringProperties()
    {
        foreach (var prop in obj.GetType().GetProperties().ToList())
        {
            if (prop.PropertyType == (typeof(string)))
            {
                if (prop.GetValue(obj) == null)
                    prop.SetValue(obj, String.Empty);
            }
        }
    }
}

class POCO : PropertyEmptySetter
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Purpose { get; set; }
}

If however, you still want to make it generic you can use a static builder method:

class Poco
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Purpose { get; set; }

    private Poco() {}

    public static Poco CreatePoco()
    {
        var poco = new Poco();
        new PropertyEmptySetter<POCO>(poco);
        return poco;
    }
}

I think you're missing key skills in C# and .NET .

Regarding your test application, first of all, it's always a terrible idea to concatenate strings in a loop. Strings are immutable in .NET , Everytime you concatenate strings, you're creating a new string. Use a StringBuilder instead

var sb = new StringBuilder();
foreach (var prop in item.GetType().GetProperties())
{
    sb.Append("Type: ");
    sb.Append(prop.Name);
    sb.Append("\tValue:");
    var value = prop.GetValue(item);
    if (value == null)
    {
        sb.Append(" NULL");
    }
    else
    {
        sb.Append(" [");
        sb.Append(value);
        sb.Append("]");
    }
    sb.Append(Environment.NewLine);
}

Console.WriteLine(sb.ToString());

Second, your implementation doesn't work and you haven't tested for what you're trying to avoid. If you try with this:

var item = new POCO {Id = 1, Name = "Brett", Description = null};

you'll get this:

Type: Id  Value: [1]
Type: Name  Value: [Brett]
Type: Description  Value: NULL
Type: Purpose  Value: []

You are deriving from a base classe only to criate a variable in the constructor that you throw away without doing anything usefull because all strings in the POCO class are all still initialized, thus null.

If you remove that from the POCO constructor:

public POCO()
{
}

and the obj from PropertyEmptySetter<T> :

public class PropertyEmptySetter<T> where T : class
{
    public PropertyEmptySetter()
    {
    }

    public void UpdateStringProperties()
    {
        foreach (var prop in this.GetType().GetProperties())
        {
            if (prop.PropertyType == (typeof(string)))
            {
                if(prop.GetValue(this) == null)
                    prop.SetValue(this, String.Empty);
            }
        }
    }
}

and use it like this:

var item = new POCO {Id = 1, Name = "Brett", Description = null};
item.UpdateStringProperties();

I think you'll get what you're looking for:

Type: Id  Value: [1]
Type: Name  Value: [Brett]
Type: Description  Value: []
Type: Purpose  Value: []

In OOP (Object Oriented Programming) the concept of hinheritance and parent-child is misleading. It's not like POCO is a child of PropertyEmptySetter<T> and PropertyEmptySetter<T> is a parent of POCO . POCO is also a PropertyEmptySetter<T> (more concretely, a PropertyEmptySetter<POCO> ), but PropertyEmptySetter<T> is not necessarily a POCO .

If you don't want to tie your POCOs to PropertyEmptySetter<T> you can add a static method to PropertyEmptySetter<T> :

public class PropertyEmptySetter<T> where T : class
{
    public void UpdateStringProperties()
    {
        UpdateStringProperties(this as T);
    }

    public static T UpdateStringProperties(T obj)
    {
        if (obj == null)
        {
            return null;
        }

        foreach (var prop in obj.GetType().GetProperties())
        {
            if (prop.PropertyType == (typeof(string)))
            {
                if(prop.GetValue(obj) == null)
                    prop.SetValue(obj, String.Empty);
            }
        }

        return obj;
    }
}

and just have:

public class POCO : PropertyEmptySetter<POCO>
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Purpose { get; set; }
}

and use it like this:

var item = new POCO {Id = 1, Name = "Brett", Description = null};
PropertyEmptySetter<POCO>.UpdateStringProperties(item);

And beacuse the static method is returning the POCO, you can chain it:

var anotherItem = PropertyEmptySetter<POCO>.UpdateStringProperties(item);

And, by the way, you are not doing anything with it that can't be done on a struct , so you should remove the class constraint and just make it an extension class:

public static class PropertyEmptySetter
{
    public static T UpdateStringProperties<T>(this T obj) where T : class
    {
        foreach (var prop in obj.GetType().GetProperties())
        {
            if (prop.PropertyType == (typeof(string)))
            {
                if(prop.GetValue(obj) == null)
                    prop.SetValue(obj, String.Empty);
            }
        }

        return obj;
    }
}

and use UpdateStringProperties like an instance method:

var item = new POCO {Id = 1, Name = "Brett", Description = null};
item = item.UpdateStringProperties();

The compiler will even infer the type in the invocation of UpdateStringProperties .

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