Переменное количество аргументов без бокса значения-типы?
public void DoSomething(params object[] args)
{
// ...
}
Проблема с приведенной выше сигнатурой заключается в том, что каждый тип значения, который будет передан этому методу, будет неявно упакован, и для меня это серьезная проблема с производительностью.
Есть ли способ объявить метод, который принимает переменное число аргументов, не помещая в скобки типы значений?
Благодарю.
5 ответов
Вы можете использовать дженерики:
public void DoSomething<T>(params T[] args)
{
}
Однако это позволит указать только один тип ValueType. Если вам нужно смешивать или сопоставлять типы значений, вам придется разрешить боксирование, как вы это делаете сейчас, или предоставить конкретные перегрузки для различного числа параметров.
Редактировать: Если вам нужно более одного типа параметра, вы можете использовать перегрузки для достижения этой цели, до некоторой степени.
public void DoSomething<T,U>(T arg1, params U[] args) {}
public void DoSomething<T,U>(T arg1, T arg2, params U[] args) {}
К сожалению, это требует множественных перегрузок для ваших типов.
В качестве альтернативы, вы можете передать массивы напрямую:
public void DoSomething<T,U>(T[] args1, U[] args2) {}
Вы теряете хороший синтаксис компилятора, но тогда вы можете передать любое количество обоих параметров.
Давайте предположим, что код, из которого вы вызываете этот метод, знает типы аргументов. Если это так, вы можете упаковать их в соответствующие Tuple
введите из.NET 4 и передайте его экземпляр (Tuple - ссылочный тип) такому методу, как object (поскольку для всех Tuples нет общей базы).
Основная проблема здесь заключается в том, что в этом методе нелегко обрабатывать аргументы без упаковки / распаковки и, вероятно, даже без отражения. Попробуйте подумать, что нужно сделать, чтобы извлечь, скажем, N-й аргумент без бокса. Вы в конечном итоге поймете, что вы должны либо иметь дело с поиском в словаре там (с участием либо обычного Dictionary<K,V>
или внутренние словари, используемые CLR), или с боксом. Очевидно, что поиск в словаре намного дороже.
Я пишу это потому, что на самом деле мы разработали решение для очень похожей проблемы: мы должны иметь возможность работать с нашими собственными кортежами без упаковки - в основном, для их сравнения и десериализации (кортежи используются разработанным нами механизмом базы данных, поэтому производительность любого базовая операция действительно необходима в нашем случае).
Но:
- Мы получаем довольно сложное решение. Посмотрите, например, на TupleComparer.
- Эффект отсутствия упаковки на самом деле не так хорош, как мы ожидали: каждая операция упаковки / распаковки заменяется индексацией одного массива и несколькими вызовами виртуальных методов, стоимость обоих способов практически одинакова.
Единственное преимущество разработанного нами подхода заключается в том, что мы не "затопляем" Gen0 мусором, поэтому коллекции Gen0 происходят гораздо реже. Поскольку стоимость сбора Gen0 пропорциональна пространству, выделенному "живыми" объектами, и их количеству, это дает заметное преимущество, если другие распределения смешиваются (или просто происходят во время) выполнения алгоритма, который мы пытаемся оптимизировать таким способом.
Результаты: после этой оптимизации наши синтетические тесты показали увеличение производительности от 0% до 200-300%; с другой стороны, простой тест производительности самого механизма базы данных показал гораздо менее впечатляющие улучшения (около 5-10%). Много времени было потрачено впустую на вышеперечисленных уровнях (также есть довольно сложный ORM), но... Скорее всего, это то, что вы действительно увидите после реализации подобных вещей.
Короче, советую сосредоточиться на чем-то другом. Если будет ясно, что это серьезная проблема с производительностью в вашем приложении, и нет других хороших способов ее решения, хорошо, продолжайте... В противном случае вы просто теряете уверенность у своего клиента или у себя, делая преждевременную оптимизацию.,
В настоящее время нет, и я не видел ничего, что решало бы проблему в информации.NET 4, которая была выпущена.
Если это огромная проблема для вас, вы можете рассмотреть несколько перегрузок часто встречающихся списков параметров.
Интересно, правда: это действительно проблема с производительностью или вы преждевременно оптимизируете?
В C# 4.0 вы можете использовать именованные (и, следовательно, необязательные) параметры! Больше информации на этом блоге
Для полностью общей реализации обычным обходным путем является использование беглого шаблона. Что-то вроде этого:
public class ClassThatDoes
{
public ClassThatDoes DoSomething<T>(T arg) where T : struct
{
// process
return this;
}
}
Теперь вы звоните:
classThatDoes.DoSomething(1).DoSomething(1m).DoSomething(DateTime.Now)//and so on
Однако это не работает со статическими классами (методы расширения в порядке, так как вы можете вернуть this
).
Ваш вопрос в основном такой же: могу ли я иметь переменное число общих параметров? спросил по-другому.
Или принять массив элементов с params
ключевое слово:
public ClassThatDoes DoSomething<T>(params T[] arg) where T : struct
{
// process
return this;
}
и позвоните:
classThatDoes.DoSomething(1, 2, 3)
.DoSomething(1m, 2m, 3m)
.DoSomething(DateTime.Now) //etc
Вопрос о том, меньше ли накладные расходы на создание массива, чем накладные расходы на упаковку, вы должны решить сами.