Сегодня ходил на собеседование, и одним из вопросов был:
Как в метод передать переменное число переменных?
Первой мыслей было использование массива. Но, к сожалению, было уточнение, что использование массивов не предусмотрено.
Мне, к сожалению, кроме использования списка и массивов ничего в голову не пришло. Интересно узнать возможные варианты.
Вопрос был в контексте С#.
P.S. Я написал: "реализовать перегрузку методов".
Ключевое слово params
-
void MyMethod(params object[] inputs)
{
}
Вызов
MyMethod(1, "dva", 3.0, false);
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params
Грешно будет не упомянуть об __arglist
!
Рассмотрим код:
public static decimal Sum(__arglist)
{
ArgIterator i = new ArgIterator(__arglist);
decimal result = 0;
while (i.GetRemainingCount() > 0)
result += (decimal)Convert.ChangeType(TypedReference.ToObject(i.GetNextArg()), typeof(decimal));
return result;
}
...
Console.WriteLine(Sum(__arglist(-1, 12L, 4U, 0.25, 0.25m, 30UL))); // 45.5
Здесь используется недокументированная возможность C#
Дело в том, что IL
, в который и компилируется наш С#, поддерживает vararg
-конвенцию (то есть можно создавать функции с динамическим числом параметров, значения которых при вызове будут взяты с вершины стека. Это Вам не params
, пакующий все переданное в массив)
Право пользоваться этим не советую: обычная упаковка в массив проходит в разы быстрее! Для чего же тогда данная возможность вообще была добавлена в C#
? Дело в том, что благодаря этому Вы спокойно можете взаимодействовать с вариативными функциями из нативных библиотек:
[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl]
static extern int Foo(__arglist); // Нативная функция с переменным числом парметров
Давайте взглянем на IL
следующего кода:
public static decimal SumParams(params object[] Objects) =>
Objects.Aggregate(0m, (sum, i) => sum + (decimal)Convert.ChangeType(i, typeof(decimal)));
public static decimal SumVarargs(__arglist)
{
ArgIterator i = new ArgIterator(__arglist);
decimal result = 0;
while (i.GetRemainingCount() > 0)
result += (decimal)Convert.ChangeType(TypedReference.ToObject(i.GetNextArg()), typeof(decimal));
return result;
}
...
SumParams(-1, 12L);
SumVarargs(__arglist(-1, 12L));
Сигнатура SumParams
:
.method public hidebysig static valuetype [mscorlib]System.Decimal SumParams(object[]) cil managed // object[] -> decimal
Вызов SumParams
:
ldc.i4.2
newarr [mscorlib]System.Object // new object[2] a
dup
ldc.i4.0
ldc.i4.m1
box [mscorlib]System.Int32
stelem.ref // a[0] = (object)(-1)
dup
ldc.i4.1
ldc.i4.s 12
conv.i8
box [mscorlib]System.Int64
stelem.ref // a[1] = (object)((long)12)
call valuetype [mscorlib]System.Decimal Program::SumParams(object[]) // сам вызов
Теперь же сигнатура SumVarargs
:
.method public hidebysig static
vararg valuetype [mscorlib]System.Decimal SumVarargs() cil managed // () -> decimal
Как видите, параметров у метода не указано вовсе! Лишь ключевое слово vararg
помогает нам понять, что это вариативная функция
Вызов SumVarargs
:
ldc.i4.m1 // -1
ldc.i4.s 12
conv.i8 // (long)12
call vararg valuetype [mscorlib]System.Decimal Program::SumVarargs(..., int32, int64) // сам вызов
Легко заметить, что никакой упаковки в массив в данном коде нет. Объекты кладутся на стек, а метод вызывается так, будто бы его сигнатура выглядит как-то так: (int, long) -> decimal
. Но на деле типы параметров указаны лишь для того, чтобы понимать что и в каком количестве забирать со стека)
Еще один вариант, пусть уж все в одном месте будет.
Если необходимо переменное число параметров из фиксированного списка, можно обойтись без перегрузки, используя для параметров значения по-умолчанию и именованное обращение к ним при вызове.
Например:
void ExampleMethod(int a = 0, double b = 0.0, string c = ""){}
Обращение:
//без именования параметров порядок при обращении важен
//Так можно
ExampleMethod(10);
ExampleMethod(10, 3.3);
ExampleMethod(10, 3.3, "");
//Так нельзя
ExampleMethod(3.3, "");
//При именованном обращении порядок не важен
ExampleMethod(a:10, b:3.3, c:"");
ExampleMethod(b:3.3, a:10, c:"");
ExampleMethod(c:"", a:10);
ExampleMethod(b:3.3);
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
При подключении через свою сеть все работает хорошо, однако если подключаться через корпоративный прокси то появляется такая ошибка:
Есть база данных, из неё делал вывод в таблицы по видеоурокамГде-то сделал ощибку, но уже второй день не могу понять где
Следующий код возвращает случайное фото из каталога:
У меня есть таблица в doctrine в формате phpВыглядит она следующим образом