简体   繁体   中英

Instantiate an instance of a Type including all object properties and adhere to inheritance

I'm not sure if that title is reflective of the actual question, so let me explain. Is there a way to instantiate a class and recursively instantiate all properties that are classes?

For example :

public class Base
{
    public int BaseValue{ get; set;} 
}

public class Extended : Base
{
    public int ExtendedValue{ get; set;}
    public AnotherExtendedClass AnotherClass { get; set;}
}

I would like to create a json payload comprised of an empty instance of Extended with all default values and properties instantiated. And use it like:

string representation = Test.CreateDefaultEmptyJson(Extended);

public static string CreateDefaultEmptyJson(Type type)
{
    JsonSerializerSettings settings = new JsonSerializerSettings().Configure();
    var defaultInstance= Activator.CreateInstance(type);
    return JsonConvert.SerializeObject(defaultInstance, settings);
}

The output does not include the Extended class properties. I get back :

{
    "BaseValue":0
}

When I would really like to see ( or something similar ):

{
    "BaseValue":0,
    {
         "ExtendedValue":0,
         {
             ...
         }
    }
}

I suppose I could recursively iterate all types of Extended and call the default constructor, however, before I go down that road there may be a few lines of code to accomplish the same.

This hastily-written class begins to address your question. It returns the settable properties which return reference types and walks through them recursively, creating instances as needed.

It doesn't cover

  • Indexed properties
  • Depth of recursion

You may be better off just setting defaults on the properties themselves so that the class won't be created with undesirable nulls.

public class PropertyPopulator
{
    public void PopulateProperties(object target)
    {
        var properties = target.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.PropertyType.IsClass && p.CanWrite && p.CanRead);
        foreach (var property in properties)
        {
            var propertyValue = property.GetValue(target);
            if (propertyValue == null)
            {
                var constructor = property.PropertyType.GetConstructor(new Type[] { });
                if (constructor != null)
                {
                    propertyValue = constructor.Invoke(new object[] { });
                    property.SetValue(target, propertyValue);
                    PopulateProperties(propertyValue);
                }
            }
        }
    }
}

As far as I know there is not a built-in way to do this short of writing your own recursive method.
However, assuming that:

  1. your classes all have parameterless (default) constructors,
  2. the non-primitive properties are all concrete types (not interfaces), and
  3. you don't have any reference loops in your class structure,

then you can create such a method in about a dozen lines of code:

public static string CreateDefaultEmptyJson(Type type)
{
    return JsonConvert.SerializeObject(RecursiveCreateInstance(type), Formatting.Indented);
}

public static object RecursiveCreateInstance(Type type)
{
    object obj = null;
    ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
    if (ctor != null)
    {
        obj = ctor.Invoke(null);
        foreach (PropertyInfo prop in type.GetProperties())
        {
            Type propType = prop.PropertyType;
            if (prop.CanWrite && propType.IsClass)
            {
                prop.SetValue(obj, RecursiveCreateInstance(propType));
            }
        }
    }
    return obj;
}

Fiddle: https://dotnetfiddle.net/3VMTsC

If the above assumptions don't hold, then things get complicated fast. If you're running into issues where you need an easy way to create fake objects for testing purposes, then you might want to look into using a mocking framework .

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