Как передать один объект [] объекту params []

124

У меня есть метод, который принимает объект params [], например:

void Foo(params object[] items)
{
    Console.WriteLine(items[0]);
}

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

Foo(new object[]{ (object)"1", (object)"2" }, new object[]{ (object)"3", (object)"4" } );
// Output: System.Object[]

Но когда я передаю один объект [], он не принимает мой объект [] в качестве первого параметра, вместо этого он принимает все его элементы, как я хотел передать их один за другим:

Foo(new object[]{ (object)"1", (object)"2" });
// Output: 1, expected: System.Object[]

Как передать один объект [] в качестве первого аргумента в массив params?

Серхат Озгель
источник

Ответы:

99

Простое приведение типов гарантирует, что компилятор знает, что вы имеете в виду в данном случае.

Foo((object)new object[]{ (object)"1", (object)"2" }));

Поскольку массив является подтипом объекта, все это работает. Хотя, я согласен, это немного странное решение.

Адам Райт
источник
2
способ работы params кажется ненужным, а дизайн C # неоптимальный, учитывая то, к чему мы привыкли в других языках. params можно было заставить принимать только одну форму, и можно было бы добавить функцию, похожую на распространение, которая принесет пользу всему языку, а не только в этом случае. например, мы могли бы заставить все вызовы параметров быть Foo (obj [0], obj [1]), а затем иметь отдельный оператор распространения, разрешающий Foo (... obj).
Whitneyland
1
Я понял, что не ясно дал понять, что я очень уважаю Андерса Хейлсберга, он один из лучших языковых дизайнеров в мире. но мы можем думать об улучшениях в чьей-либо работе, если оглянуться назад, а значит, и технологии.
whitneyland
74

paramsМодификатор параметра дает звонящему ярлык синтаксис для передачи несколько аргументов для метода. Есть два способа вызвать метод с paramsпараметром:

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

object[] array = new[] { "1", "2" };

// Foo receives the 'array' argument directly.
Foo( array );

2) Или вызов с расширенным списком аргументов, и в этом случае компилятор автоматически обернет список аргументов во временный массив и передаст его методу:

// Foo receives a temporary array containing the list of arguments.
Foo( "1", "2" );

// This is equivalent to:
object[] temp = new[] { "1", "2" );
Foo( temp );


Чтобы передать массив объектов методу с params object[]параметром " ", вы можете:

1) Создайте массив-оболочку вручную и передайте его непосредственно методу, как указано в lassevk :

Foo( new object[] { array } );  // Equivalent to calling convention 1.

2) Или приведите аргумент к object, как упомянул Адам , и в этом случае компилятор создаст для вас массив-оболочку:

Foo( (object)array );  // Equivalent to calling convention 2.


Однако, если целью метода является обработка нескольких массивов объектов, может быть проще объявить его с явным " params object[][]" параметром. Это позволит вам передать несколько массивов в качестве аргументов:

void Foo( params object[][] arrays ) {
  foreach( object[] array in arrays ) {
    // process array
  }
}

...
Foo( new[] { "1", "2" }, new[] { "3", "4" } );

// Equivalent to:
object[][] arrays = new[] {
  new[] { "1", "2" },
  new[] { "3", "4" }
};
Foo( arrays );

Изменить: Раймонд Чен описывает это поведение и его связь со спецификацией C # в новом посте .

Император XLII
источник
8

Это однострочное решение, включающее LINQ.

var elements = new String[] { "1", "2", "3" };
Foo(elements.Cast<object>().ToArray())
ACOMIT001
источник
3

Вам нужно инкапсулировать его в другой массив object [], например:

Foo(new Object[] { new object[]{ (object)"1", (object)"2" }});
Лассе В. Карлсен
источник
2

Другой способ решить эту проблему (это не очень хорошая практика, но выглядит красиво):

static class Helper
{
    public static object AsSingleParam(this object[] arg)
    {
       return (object)arg;
    }
}

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

f(new object[] { 1, 2, 3 }.AsSingleParam());
Журавлев А.
источник
1

Один из вариантов - вы можете обернуть его в другой массив:

Foo(new object[]{ new object[]{ (object)"1", (object)"2" } });

Это некрасиво, но поскольку каждый элемент представляет собой массив, вы не можете просто использовать его, чтобы проблема исчезла ... например, если бы это был Foo (элементы объекта params), тогда вы могли бы просто сделать:

Foo((object) new object[]{ (object)"1", (object)"2" });

В качестве альтернативы вы можете попробовать определить другой перегруженный экземпляр Foo, который принимает только один массив:

void Foo(object[] item)
{
    // Somehow don't duplicate Foo(object[]) and
    // Foo(params object[]) without making an infinite
    // recursive call... maybe something like
    // FooImpl(params object[] items) and then this
    // could invoke it via:
    // FooImpl(new object[] { item });
}
Майк Стоун
источник
1
new[] { (object) 0, (object) null, (object) false }
Гомер Барбоза
источник