Jak przekazać pojedynczy obiekt [] do obiektu params []

124

Mam metodę, która przyjmuje parametry obiektu [], na przykład:

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

Kiedy przekazuję dwie tablice obiektów do tej metody, działa dobrze:

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

Ale kiedy przekazuję pojedynczy obiekt [], nie bierze on mojego obiektu [] jako pierwszego parametru, zamiast tego pobiera wszystkie jego elementy, tak jak chciałem przekazać je jeden po drugim:

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

Jak przekazać pojedynczy obiekt [] jako pierwszy argument do tablicy params?

Serhat Ozgel
źródło

Odpowiedzi:

99

Proste rzutowanie typu zapewni, że kompilator wie, co masz na myśli w tym przypadku.

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

Ponieważ tablica jest podtypem obiektu, wszystko działa. Jednak trochę dziwne rozwiązanie, zgadzam się.

Adam Wright
źródło
2
sposób działania parametrów wydaje się niepotrzebny i nieoptymalny projekt C #, biorąc pod uwagę to, do czego przyzwyczailiśmy się w innych językach. params można było sprawić, by akceptował tylko jeden formularz i można by dodać funkcję podobną do rozszerzania, która przyniosłaby korzyści całemu językowi, a nie tylko temu przypadkowi. na przykład, moglibyśmy wymusić, aby wszystkie wywołania parametrów były Foo (obj [0], obj [1]), a następnie mieć oddzielny operator spreadu pozwalający na Foo (... obj).
whitneyland
1
zdał sobie sprawę, że nie powiedziałem jasno, że mam wielki szacunek dla andersa hejlsberga, jest jednym z najlepszych projektantów językowych na świecie. ale z perspektywy czasu możemy pomyśleć o ulepszeniach w każdej pracy, stąd technologia.
whitneyland
74

paramsModyfikujący parametr podaje rozmówcy na składnie skrótów dla przekazywania wielu argumentów metody. Istnieją dwa sposoby wywołania metody z paramsparametrem:

1) Wywołanie z tablicą typu parametru, w którym to przypadku paramssłowo kluczowe nie działa i tablica jest przekazywana bezpośrednio do metody:

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

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

2) Lub wywołanie z rozszerzoną listą argumentów, w którym to przypadku kompilator automatycznie zawinie listę argumentów w tymczasowej tablicy i przekaże ją do metody:

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

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


Aby przekazać tablicę obiektów do metody z params object[]parametrem „ ”, możesz:

1) Utwórz ręcznie tablicę opakowującą i przekaż ją bezpośrednio do metody, jak wspomniał lassevk :

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

2) Lub przerzuć argument na object, jak wspomniał Adam , w takim przypadku kompilator utworzy tablicę opakowującą:

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


Jeśli jednak celem metody jest przetwarzanie wielu tablic obiektów, może być łatwiej zadeklarować ją z jawnym params object[][]parametrem " ". Umożliwiłoby to przekazanie wielu tablic jako argumentów:

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

Edycja: Raymond Chen opisuje to zachowanie i jego związek ze specyfikacją C # w nowym poście .

Cesarz XLII
źródło
8

Jest to jedno liniowe rozwiązanie obejmujące LINQ.

var elements = new String[] { "1", "2", "3" };
Foo(elements.Cast<object>().ToArray())
ACOMIT001
źródło
3

Musisz umieścić go w innej tablicy obiektów [], na przykład:

Foo(new Object[] { new object[]{ (object)"1", (object)"2" }});
Lasse V. Karlsen
źródło
2

Inny sposób rozwiązania tego problemu (nie jest to dobra praktyka, ale wygląda pięknie):

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

Stosowanie:

f(new object[] { 1, 2, 3 }.AsSingleParam());
Zhuravlev A.
źródło
1

Jedną z opcji jest to, że możesz umieścić go w innej tablicy:

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

Trochę brzydkie, ale ponieważ każdy element jest tablicą, nie możesz go tak po prostu rzucić, aby problem zniknął ... na przykład gdyby to było Foo (elementy obiektu params), możesz po prostu zrobić:

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

Alternatywnie możesz spróbować zdefiniować inną przeciążoną instancję Foo, która zajmuje tylko jedną tablicę:

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 });
}
Mike Stone
źródło
1
new[] { (object) 0, (object) null, (object) false }
Homero Barbosa
źródło