C # как передать необязательные параметры в виде коллекции? - PullRequest
0 голосов
/ 07 ноября 2019

Предполагая, что у меня есть сторонняя организация MethodA(type1 paramName1 = null, type2 paramName2 = null,...., nonNullAbleSuchAsInt paramName10 = 123).

И я хочу сделать следующее, вызывая тот же метод A с использованием предоставленных данных, предоставленных пользователем. Пользователь может предоставить мне данные для paramName2 и paramName4 или некоторых других перестановок, таких как [paramName2, paramName3, paramName5, paramName7] и т. Д.

if(user gives me data for paramName2 and nothing else)
{
  MethodA(paramName2=userData2)
}
else if(user give me data for paramName2, paramName3, and nothing else)
{
  MethodA(paramName2=userData2, paramName3=userData3)
}
else if(user give me data for paramName2, paramName4, and nothing else)
{
  MethodA(paramName2=userData2, paramName4=userData4)
}
else if(user give me data for paramName2, paramName3, paramName4, and nothing else)
{
  MethodA(paramName2=userData2, paramName3=userData3, paramName4=userData4)
}
... repeat for all permutations.

Но это так много дублированного кода.

Я хочу сделать следующее. Как мне это сделать?

MagicStorage<MethodA_Declaration> magicArgs = new MagicStorage<MethodA_Declaration>();

if(user gives me data for paramName1)
{
  magicArgs.Add(paramName1, userData1);
}

if(user gives me data for paramName2)
{
  magicArgs.Add(paramName2, userData2);
}

... repeat

if(user gives me data for paramName10)
{
  magicArgs.Add(paramName10, userData10);
}

MethodA(magicArgs);

И если я допустил ошибку, такую ​​как userData10 не того типа, который необходим paramName10, я получаю ошибку редактора и компилятора.

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

Ответы [ 4 ]

3 голосов
/ 07 ноября 2019

Дано

MethodA(type1 paramName1 = null, type2 paramName2 = null, int paramName10 = 132).

Использование

public class data
{
    public type1 ParamName1 {get;set;}
    public type2 paramName2 {get;set;}
    // initalize with what ever default the method takes as optional
    public int paramName10 {get;set;} = 123; 
}

...

// pass in all paramaters, dont both about ifs
// we can do this, because you have already figured out the defaults
// from the signature 
MethodA(data.ParamName1, data.paramName2, data.paramName10);
1 голос
/ 08 ноября 2019

Одним из способов было бы использование отражения для вызова метода с упорядоченным массивом параметров, где заданные пользователем значения используются там, где это возможно, и Type.Missing используется там, где не было указано значение:

public static object InvokeWithOrderedParameters(object instance, string methodName,
    IDictionary<string, object> namedParameters)
{
    // Get the method to invoke
    var method = instance.GetType().GetMethod(methodName);

    // Get an array of ordered parameter values based on the specified named 
    // parameters, with a default value of "Type.Missing" for any missing names 
    var orderedParams = method.GetParameters().Select(param =>
    {
        object value;

        // Set the value from our dictionary, or if that fails use "Type.Missing"
        if (!namedParameters.TryGetValue(param.Name, out value))
        {
            value = Type.Missing;
        }

        return value;
    }).ToArray();

    // Invoke the method with the ordered parameters and return the value
    return method.Invoke(instance, orderedParams);
}

С помощью этого метода мы можем передать экземпляр нашего типа, имя вызываемого метода и Dictionary<string, object> именованных параметров и их значений, и он вернет результат вызова этого метода с параметрами, которые мы указали.

В качестве примера, вот метод, который имеет значения по умолчанию для всех своих параметров:

public class ThirdParty
{
    public string MethodA(string arg1 = "defaultArg1", string arg2 = "defaultArg2",
        string arg3 = "defaultArg3")
    {
        return $"{arg1}, {arg2}, {arg3}";
    }
}

И мы можем использовать наш метод отражения, чтобы вызывать его с таким количеством именованных параметров, как нам нравится. Ниже я просто даю значение для второго параметра:

public static void Main(string[] args)
{
    var namedParameters = new Dictionary<string, object>
    {
        {"arg2", "custom Arg 2 value"}
    };

    var instance = new ThirdParty();
    var result = InvokeWithOrderedParameters(instance, "MethodA", namedParameters);

    Console.WriteLine(result.ToString());

    GetKeyFromUser("\nDone! Press any key to exit...");
}

Вывод

Как видите, указанное нами значение было передано, и значение по умолчаниюзначения использовались там, где мы ничего не указывали:

enter image description here

0 голосов
/ 07 ноября 2019

У вас может быть что-то вроде

int param1 = default(int);
double param2 = default(double);
MyClass param3 = default(MyClass);
 ....
int param10 = 123;
ReadUserData(out param1, out param2, out param3, ..., out param10)

Затем вызовите MethodA:

MethodA(param1, param2, param3, ..., param10)

ПРИМЕЧАНИЕ. Начиная с C # 7.0 (я думаю) при использовании параметров out естьсахар, который позволяет вам объявлять переменные с помощью вызова метода (так что вам не нужно объявлять их по одной), например, вызов ReadUserData может быть выполнен как:

ReadUserData(out int param1, out double param2, out MyClass param3, ..., out int param10)
0 голосов
/ 07 ноября 2019

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

И, конечно, вы можете заменить любой переключатель на словарь и цикл, если значение имеет место для делегата функции. Конечно, в этом случае ключу может потребоваться и делегат.

Это сделало бы код более читабельным, но не смогло бы избежать работы по записи.

Помимо этого, существует только перемещение разрешения вызовов функций из времени компиляции во время выполнения. Шаг, с которым я не дружу, и поэтому у меня мало опыта.

...