如何创建(实际上)任何给定类型的通用“示例”对象?

时间:2012-01-27 21:09:09

标签: .net reflection activator

我正在开发一个基本上自动生成REST API文档的元数据生成器。

部分内容包括显示请求/响应类型,当然可以是DTO。我想要的是对象的序列化JSON(或XML)版本,显示结构和占位符数据。 (序列化部分很简单,它创建的对象开始时很难)。例如,给定对象:

public class MyObject {
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Active { get; set; }
}

我希望能够调用某个函数:

var obj = GetDefaultValue(typeof(MyObject));

并获得相当于:

new MyObject { Name = string.Empty, Age = 0, Active = false };
// or in other words:  new MyObject { Name = default(string), Age = default(int), Active = default(bool) };

我有一些基本的代码开始这样做:

    /// <summary>
    /// Class to create a default value for a specified type.
    /// </summary>
    public abstract class DefaultValueGenerator
    {
        private DefaultValueGenerator() { }

        /// <summary>
        /// Creates a new default instance of type T.
        /// Requires that T has a parameter-less constructor, or can be created using <code>default(T)</code>
        /// </summary>
        /// <param name="T"></param>
        /// <returns></returns>
        public static object GetDefaultValue(Type T)
        {
            try
            {
                return System.Activator.CreateInstance(T, true);
                //TODO: if array type, also create a single item of that type
            }
            catch (Exception activatorException)
            {
                try
                {
                    // from http://stackoverflow.com/a/2490267/7913
                    var defaultGeneratorType = typeof(DefaultGenerator<>).MakeGenericType(T);

                    return defaultGeneratorType.InvokeMember(
                      "GetDefault",
                      BindingFlags.Static |
                      BindingFlags.Public |
                      BindingFlags.InvokeMethod,
                      null, null, new object[0]);
                }
                catch //(Exception defaultGeneratorException)
                {
                    throw new MissingMethodException(string.Format("No parameterless constructor defined for model {0}", T.Name), activatorException);
                }

            }

        }

        // from http://stackoverflow.com/a/2490267/7913
        private class DefaultGenerator<T>
        {
            public static T GetDefault()
            {
                return default(T);
            }
        }


    }

所以,使用这个你可以打电话:

var obj = DefaultValueGenerator.GetDefaultValue(typeof(MyObject));

此实现的一个问题是,如果您调用DefaultValueGenerator.GetDefaultValue(typeof(string)),它会将我捕获的异常抛出为activatorException,然后使用default关键字。只是丑陋,因为我依赖于例外......有更好的方法吗?


第二个问题是数组/集合。例如:DefaultValueGenerator.GetDefaultValue(typeof(List<MyObject>))创建一个0元素列表,然后将序列化为JSON [] - 在文档方面不是很有用。我希望这能生成一个元素。


第三个问题是嵌套类型。例如,如果我有:

public class MyContainerObject {
    public MyObject OtherObject { get; set; }
    public int SomeValue { get; set; }
}

我希望这能产生相当于:

var obj = new MyContainerObject { 
    OtherObject = new MyObject { Name = string.Empty, Age = 0, Active = false },
    SomeValue = 0,
}

但实际上,它会将OtherObject生成为空值。


有人知道某些代码/库已经这样做了吗?否则,有关如何实现这一目标的任何提示,并避免我指出的一些陷阱?有没有不同的方法来解决这个问题会更容易?

我希望这适用于内置的基本类型(string,int,guid等)以及任何更复杂的对象 - 只要它们有一个无参数的构造函数(我没关系限制,因为使用的类型应该是POCO / DTO的。)

1 个答案:

答案 0 :(得分:0)

完成重写

我看到你有DefaultGenerator的源代码。你可以创建一个TryMakeGenericType重载来消除丑陋的try ... catch块。这应该使代码更优雅。它不会消除抛出异常的事实,但它会隐藏它。

关于列表,是否无法确定列表中存储的项目类型?我想如果你有这个,那么渲染代码以插入代码来初始化该类型的成员应该是一个相对简单的事情。

嵌套类型似乎与列表中遇到的问题相同。事实上,我敢打赌,一旦你解决了这个问题,对方的解决方案就会变得很明显。但是,如果没有看到所有来源,那就很难说了。

我同意,拥有一个已经完成此功能的库是非常好的。我希望在这里为我们的XML API找到类似的东西。你可能需要在短期内扩展你拥有的东西,直到找到合适的替代品。