Как я могу сделать этот метод короче и легче следовать?


Это длинные, однообразные, и трудно следовать.

    private void Map(object x, JToken json)
    {
        var objType = x.GetType();
        var props = objType.GetProperties().Where(p => p.CanWrite).ToList();

        foreach (var prop in props)
        {
            var type = prop.PropertyType;

            var name = prop.Name;
            var value = json[name];
            var actualName = name;

            if (value == null)
            {
                // try camel cased name
                actualName = name.ToCamelCase(Culture);
                value = json[actualName];
            }

            if (value == null)
            {
                // try lower cased name
                actualName = name.ToLower(Culture);
                value = json[actualName];
            }

            if (value == null)
            {
                // try name with underscores
                actualName = name.AddUnderscores();
                value = json[actualName];
            }

            if (value == null)
            {
                // try name with underscores with lower case
                actualName = name.AddUnderscores().ToLower(Culture);
                value = json[actualName];
            }

            if (value == null)
            {
                // try name with dashes
                actualName = name.AddDashes();
                value = json[actualName];
            }

            if (value == null)
            {
                // try name with dashes with lower case
                actualName = name.AddDashes().ToLower(Culture);
                value = json[actualName];
            }

            if (value == null || value.Type == JTokenType.Null)
            {
                continue;
            }

            // check for nullable and extract underlying type
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                type = type.GetGenericArguments()[0];
            }

            if (type.IsPrimitive)
            {
                // no primitives can contain quotes so we can safely remove them
                // allows converting a json value like {"index": "1"} to an int
                var tmpVal = value.AsString().Replace("\"", string.Empty);
                prop.SetValue(x, tmpVal.ChangeType(type), null);
            }
            else if (type.IsEnum)
            {
                string raw = value.AsString();
                var converted = Enum.Parse(type, raw, false);
                prop.SetValue(x, converted, null);
            }
            else if (type == typeof(Uri))
            {
                string raw = value.AsString();
                var uri = new Uri(raw, UriKind.RelativeOrAbsolute);
                prop.SetValue(x, uri, null);
            }
            else if (type == typeof(string))
            {
                string raw = value.AsString();
                prop.SetValue(x, raw, null);
            }
            else if (type == typeof(DateTime) || type == typeof(DateTimeOffset))
            {
                DateTime dt;
                if (DateFormat.HasValue())
                {
                    var clean = value.AsString();
                    dt = DateTime.ParseExact(clean, DateFormat, Culture);
                }
                else if (value.Type == JTokenType.Date)
                {
                    dt = value.Value<DateTime>().ToUniversalTime();
                }
                else
                {
                    // try parsing instead
                    dt = value.AsString().ParseJsonDate(Culture);
                }

                if (type == typeof(DateTime))
                    prop.SetValue(x, dt, null);
                else if (type == typeof(DateTimeOffset))
                    prop.SetValue(x, (DateTimeOffset)dt, null);
            }
            else if (type == typeof(Decimal))
            {
                var dec = Decimal.Parse(value.AsString(), Culture);
                prop.SetValue(x, dec, null);
            }
            else if (type == typeof(Guid))
            {
                string raw = value.AsString();
                var guid = string.IsNullOrEmpty(raw) ? Guid.Empty : new Guid(raw);
                prop.SetValue(x, guid, null);
            }
            else if (type.IsGenericType)
            {
                var genericTypeDef = type.GetGenericTypeDefinition();
                if (genericTypeDef == typeof(List<>))
                {
                    var list = BuildList(type, value.Children());
                    prop.SetValue(x, list, null);
                }
                else if (genericTypeDef == typeof(Dictionary<,>))
                {
                    var keyType = type.GetGenericArguments()[0];

                    // only supports Dict<string, T>()
                    if (keyType == typeof(string))
                    {
                        var dict = BuildDictionary(type, value.Children());
                        prop.SetValue(x, dict, null);
                    }
                }
                else
                {
                    // nested property classes
                    var item = CreateAndMap(type, json[actualName]);
                    prop.SetValue(x, item, null);
                }
            }
            else
            {
                // nested property classes
                var item = CreateAndMap(type, json[actualName]);
                prop.SetValue(x, item, null);
            }
        }
    }


1592
7
c#
задан 18 октября 2011 в 04:10 Источник Поделиться
Комментарии
4 ответа

Чтобы развернуть на Карла ответ немного больше, это будет хорошим кандидатом для стратегии шаблон. Можно добавить каждый случай как стратегия обратного вызова:

private var toValue(var name, params Func<var, var>[] matchingStrategies)
{
foreach(var strategy in matchingStrategies)
{
value = json[strategy(name)];

if (value != null)
return value;
}

return null;
}

// usage:

var name = prop.Name;

var value = toValue(name,
(n) => n.ToCamelCase(Culture),
(n) => n.ToLower(Culture),
(n) => n.AddUnderscores(),
(n) => n.AddUnderscores().ToLower(Culture),
(n) => n.AddDashes(),
(n) => n.AddDashes().ToLower(),
(n) => n.ToCamelCase(Culture));

Так вы передаете список подходящих стратегий для того, выполнения, с функция, возвращающая результат первого успешного подбора стратегии.

Или вы могли бы инкапсулировать каждую стратегию в свой собственный класс - который позволит вам полностью проверить их по отдельности.

19
ответ дан 19 октября 2011 в 09:10 Источник Поделиться

Шаг 1: извлечь тело цикла в отдельный метод, который принимает в качестве параметра реквизит. Сейчас основной способ выглядит

private void Map(object x, JToken json)
{
var objType = x.GetType();
var props = objType.GetProperties().Where(p => p.CanWrite).ToList();

foreach (var prop in props)
{
process(prop);
}
}

и это хорошее начало.

Сейчас мы проводим большое количество кода для получения значения имени. Экстракт в том, что:

private var toValue(var name)
{
var value = json[name];
if (value != null) return value;

value = json[name.ToCamelCase(Culture)];
if (value != null) return value;

value = json[name.ToLower(Culture)];
if (value != null) return value;

value = json[name.AddUnderscores()];
if (value != null) return value;

value = json[name.AddUnderscores().ToLower(Culture)];
if (value != null) return value;

value = json[name.AddDashes()];
if (value != null) return value;

value = json[name.AddDashes().ToLower(Culture)];
if (value != null) return value;

value = json[name.ToCamelCase(Culture)];
if (value != null) return value;

return null;
}

Обратите внимание, насколько чище в этом разделе может быть, потому что мы извлекли его. Мы просто возвращать значение, если это не null, а не снова проверки, снова и снова, будь то нуль. И этот извлеченный метод имеет четкую единую цель; если вы придумали другой вариант, как процесс имя, чтобы сделать его действительным ключом, было бы понятно, куда ее девать. Меньше способов расплатиться во многих отношениях.

Я думаю, что это хорошее начало, и вы должны быть в состоянии видеть, куда идти отсюда.

11
ответ дан 18 октября 2011 в 05:10 Источник Поделиться

Я бы тоже брось .Список() в этой строке:

var props = objType.GetProperties().Where(p => p.CanWrite).ToList();

ведь реквизит используется только в одном месте, должны быть пронумерованы в цикле foreach петли.

Позвонив в список заставляет компьютер, чтобы петля через всю коллекцию, просто чтобы преобразовать его в список.

Затем на следующей строке он прокручивает весь путь до конца во второй раз. Зачем всю эту работу только, чтобы получить список?

Код будет вести себя так же, только быстрее, если мы просто цикл по коллекции

5
ответ дан 1 февраля 2012 в 12:02 Источник Поделиться

Я лично стараюсь держаться подальше от использования ВАР сайта, а иногда как правило, делают код нечитаемым, например ВАР результат = ReturnResult(); при чтении этого я не знаю, что типа это на первый взгляд. И попробовать инициализировать свои переменные вне цикла foreach петли. Я обычно заявляю, все мои переменные в верхней части метод и использовать их позже при необходимости.

-4
ответ дан 15 января 2014 в 09:01 Источник Поделиться