Как я могу создать общий «пример» объекта (практически) любого данного типа? - PullRequest
0 голосов
/ 28 января 2012

Я работаю над генератором метаданных, который в основном автоматически генерирует документацию для 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 голосов
/ 28 января 2012

ЗАВЕРШЕНО Перепишите

Я вижу, что у вас есть исходный код DefaultGenerator. Вы можете создать перегрузку TryMakeGenericType, чтобы устранить уродливый блок try ... catch. Это должно сделать код более элегантным. Это не устранит тот факт, что создается исключение, но оно скрывает его.

Что касается списков, нет ли способа определить тип элементов, хранящихся в списке? Я думаю, что, как только вы это сделаете, должно быть относительно простым сделать код, чтобы вставить код для инициализации члена этого типа.

Вложенные типы - это та же проблема, с которой вы сталкиваетесь со списками. На самом деле, я бы держал пари, что, как только вы решите одну проблему, решение другой станет очевидным. Однако, не видя всего источника, трудно сказать.

Я согласен, было бы очень хорошо иметь библиотеку, которая уже делает это. Я хотел бы найти что-то подобное для нашего XML API здесь. Скорее всего, вам придется расширить то, что у вас есть, в краткосрочной перспективе, пока не будет найдена подходящая альтернатива.

...