1

Imagine there is a simple method like this:

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);
}

This is my first design. I will make a lot of functions like that from now. But the code has something I don't like. It is the part of adding parameters to a dictionary. I think it is obvious code duplication.

  • All parameter names are used as the dictionary's keys.
  • And they all will implement their own ToString() method.
  • If parameter is null, it shouldn't be put in dictionary.

It would be better, if it could:

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();

How to make this code using C#(5.0) syntax? If you have a better suggestion, I will be happy to hear.
If it is impossible, is that possible wrapping it with macro?(as we often did when we write C) Tell me any possible idea to deduplicate the code :)

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Benjamin
  • 10,085
  • 19
  • 80
  • 130

2 Answers2

0

You could try this approach:

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);
}
0

Why don't you use a DTO instead? It would make serialization a lot easier.

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;
    }
}

You don't even need to change the way you call it much if you use object initialization syntax:

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);
}

You don't even need to create a type for it:

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
    });
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
  • 1
    The problem with the `object` approach is that it's not type-safe. If you forget to specify one of the parameters, or mistype its name, you're not going to get a compile error. – svick Jul 07 '14 at 15:15