Как извлечь имена параметров метода и использовать их в качестве ключей словаря?

Представьте, что есть такой простой метод:

public async Task<ValidatePhoneNumberResult> ValidatePhoneNumberAsync(
    string phone_number,
    string country_code,
    string country_iso,
    DeviceUuid device_uuid, // DeviceUuid supports his own ToString();
    string os_name,
    string os_version,
    string model,
    string screen_resolution,
    string sim_operator = "00000",
    string is_usim = null
    )
{
    Uri uri = new Uri(MY_URI);
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);

    Dictionary<string, string> dic = new Dictionary<string, string>();
    dic.Add("phone_number", phone_number.ToString());
    dic.Add("country_code", country_code.ToString());
    dic.Add("country_iso", country_iso.ToString());
    dic.Add("os_name", os_name.ToString());
    dic.Add("model", model.ToString());
    dic.Add("screen_resolution", screen_resolution.ToString());
    dic.Add("sim_operator", sim_operator.ToString());
    if (is_usim != null)
    {
        dic.Add("is_usim", is_usim.ToString());
    }

    request.Content = new FormUrlEncodedContent(dic);
    return await GetResult<ValidatePhoneNumberResult>(request);
}

Это мой первый дизайн. С этого момента я сделаю много таких функций. Но в коде есть кое-что, что мне не нравится. Это часть добавления параметров в словарь. Думаю, это явное дублирование кода.

  • Все имена параметров используются в качестве ключей словаря.
  • И все они будут реализовывать свой собственный метод ToString ().
  • Если параметр null, его не следует помещать в словарь.

Было бы лучше, если бы он мог:

Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("phone_number", phone_number.ToString());
dic.Add("country_code", country_code.ToString());
dic.Add("country_iso", country_iso.ToString());
dic.Add("os_name", os_name.ToString());
dic.Add("model", model.ToString());
dic.Add("screen_resolution", screen_resolution.ToString());
dic.Add("sim_operator", sim_operator.ToString());
if (is_usim != null)
{
    dic.Add("is_usim", is_usim.ToString());
}

// To
var dic = ExtractParametersAndMakeItAsDictionary();

Как сделать этот код с использованием синтаксиса C # (5.0)? Если у вас есть предложение получше, я буду счастлив услышать его.
Если это невозможно, можно ли обернуть его _4 _? (Как мы часто делали, когда писали C). Сообщите мне любую возможную идею дедупликации кода: )


person Benjamin    schedule 24.03.2014    source источник
comment
К сожалению, не похоже, что это можно сделать легко: stackoverflow.com/questions/1867482/   -  person Matthew    schedule 24.03.2014
comment
@Matthew Спасибо. Вопрос немного изменен.   -  person Benjamin    schedule 24.03.2014
comment
В C # нет макросов.   -  person svick    schedule 24.03.2014


Ответы (2)


Вы можете попробовать такой подход:

public void SomeMethod(string p1, int p2, object p3)
{
    Dictionary<string, string> dic = ExtractParametersAndMakeItAsDictionary(p1, p2, p3);
}

private Dictionary<string, string> ExtractParametersAndMakeItAsDictionary(params object[] parameters)
{
    StackTrace stackTrace = new StackTrace();
    string methodName = stackTrace.GetFrame(1).GetMethod().Name;
    ParameterInfo[] parameterInfos = GetType().GetMethod(methodName).GetParameters();

    return parameters.Where(p => p != null).Zip(parameterInfos, (pv, pi) => new { Name = pi.Name, Value = pv.ToString() }).ToDictionary(x => x.Name, x => x.Value);
}
person Dmitriy Finozhenok    schedule 24.03.2014

Почему бы вам не использовать вместо этого DTO? Это значительно упростило бы сериализацию.

public static class Extensions
{
    public static Dictionary<string, string> ToDicionary(this object o)
    {
        var dic = new Dictionary<string, string>();
        foreach(var property in o.GetType().GetProperties())
        {
            dic.Add(property.Name, string.Format("{0}", property.GetValue(o)));
        }

        return dic;
    }
}

Вам даже не нужно сильно менять способ его вызова, если вы используете синтаксис инициализации объекта:

public async Task<ValidatePhoneNumberResult> ValidatePhoneNumberAsync(object dto)
{
    Uri uri = new Uri(MY_URI);
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);

    request.Content = new FormUrlEncodedContent(dto.ToDictionary());
    return await GetResult<ValidatePhoneNumberResult>(request);
}

Для него даже не нужно создавать тип:

var result = await ValidatePhoneNumberAsync(
    new
    {
            phone_number = "000000000",
            country_code = "code",
            country_iso = "iso",
            device_uuid = new DeviceUuid(),
            os_name = "Windows",
            os_version = "6.3",
            model = "model",
            screen_resolution = "4K",
            sim_operator = "00000",
            is_usim = null
    });
person Paulo Morgado    schedule 24.03.2014
comment
Проблема подхода object в том, что он небезопасен по типу. Если вы забудете указать один из параметров или неправильно введете его имя, вы не получите ошибки компиляции. - person svick; 07.07.2014