76

Is there any implementation in C# like JavaScript's spread syntax?

var arr = new []{"Hello", "World"};
Console.WriteLine(...arr);

3rd party edit

Using a method

public void greet(string salutation, string recipient)
{
    Console.WriteLine(salutation + " " + recipient);    
}

// instead of this
greet(arr[0], arr[1]);
// the spread syntax in javascript allows this
greet(...arr);
8
  • 4
    Passing an array to params is as close as you're going to get. Commented Oct 6, 2016 at 3:41
  • 1
    Method with keyword params in parameter will be more much likely an answer. Thanks @Rob Commented Oct 6, 2016 at 3:43
  • 8
    @KeithNicholas i think it does make sense in other contexts eg ctx.users.Select(u => new { u.id, u.otherfields } ).ToList().ConvertAll(u => new { ...u, someList.FirstOrDefault(l => l.userid == u.id).something}) Commented Dec 5, 2017 at 18:16
  • 1
    Small nitpick, ... syntax is not an operator. In the specification, it is referred to in the language grammar as SpreadElement, though informally called the "spread syntax" since it is not a context-free grammar. Commented Aug 1, 2018 at 17:47
  • 2
    (a, b, ...others) = getTwoParamsAndOthersIntoArray() - not sure why this syntax wouldn't make less sense in C# than JS now it has dynamics, value tuples and deconstruction :) Commented Apr 16, 2019 at 19:10

6 Answers 6

37

C# 12 has added the spread feature.

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[] single = [..row0, ..row1, ..row2];
foreach (var element in single)
{
    Console.Write($"{element}, ");
}

https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12

Old answer (pre C# 12)

There isn't a spread option, but some useful alternatives.

  1. Method Parameters aren't an array in C# unless you use the params keyword
  2. Method Parameters that use the param keyword would have to either:
    1. Share the same type
    2. Have a castable shared type such as double for numerics
    3. Be of type object[] (as object is the root type of everything)

However, having said that, you can get similar functionality with various language features.

Answering your example:

C#

var arr = new []{
   "1",
   "2"//...
};

Console.WriteLine(string.Join(", ", arr));

The link you provide has this example:

Javascript Spread

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// expected output: 6

console.log(sum.apply(null, numbers));

Params In C#, with same type

public int Sum(params int[] values)
{
     return values.Sum(); // Using linq here shows part of why this doesn't make sense.
}

var numbers = new int[] {1,2,3};

Console.WriteLine(Sum(numbers));

In C#, with different numeric types, using double

public int Sum(params double[] values)
{
     return values.Sum(); // Using linq here shows part of why this doesn't make sense.
}

var numbers = new double[] {1.5, 2.0, 3.0}; // Double usually doesn't have precision issues with small whole numbers

Console.WriteLine(Sum(numbers));

Reflection In C#, with different numeric types, using object and reflection, this is probably the closest to what you are asking for.

using System;
using System.Reflection;

namespace ReflectionExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var paramSet = new object[] { 1, 2.0, 3L };
            var mi = typeof(Program).GetMethod("Sum", BindingFlags.Public | BindingFlags.Static);
            Console.WriteLine(mi.Invoke(null, paramSet));
        }

        public static int Sum(int x, double y, long z)
        {
            return x + (int)y + (int)z;
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Your example with reflection is actually a lot more akin to JavaScript's Function.prototype.apply(), which was a great pre-ES6 feature that was used a lot before the spread syntax was available, but great suggestion regardless (although I wouldn't recommend it in performance-critical production code).
The double sample needs an explicit conversion to int return (int) values.Sum();. The sum of the input var numbers = new double[] {1.5, 2.0, 3.0}; is 6 since the sum 6,5 is not of type int. When you convert a double or float value to an integral type, this value is rounded towards zero to the nearest integral value.
c# 12 has a spread operator.
Thanks! C# team adds cool new features with each release.
15

C# 12 has introduced the spread operator similar to Javascript. It is available with .Net 8

We can write

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[] single = [..row0, ..row1, ..row2];

3 Comments

.. (<- look a spread operator ;) ) and normally today, 11/14/2023, is the release date of .NET 8.
@Timmos Hurray :D Which means I should update the answer ;)
It has indeed launched and is installed together with (update to) Visual Studio 17.8.
7

One trick to get a behavior similar to this (without reflection) is to accept params SomeObject[][] and to also define an implicit operator from SomeObject to SomeObject[]. Now you can pass a mixture of arrays of SomeObject and individual SomeObject elements.

public class Item
{
    public string Text { get; }

    public Item (string text)
    {
        this.Text = text;
    }

    public static implicit operator Item[] (Item one) => new[] { one };
}

public class Print
{
    // Accept a params of arrays of items (but also single items because of implicit cast)

    public static void WriteLine(params Item[][] items)
    {
        Console.WriteLine(string.Join(", ", items.SelectMany(x => x)));
    }
}

public class Test
{
    public void Main()
    {
        var array = new[] { new Item("a1"), new Item("a2"), new Item("a3") };
        Print.WriteLine(new Item("one"), /* ... */ array, new Item("two")); 
    }
}

Comments

3

there is no direct pre-built library in C# to handle what is built into Spread

In order to get that functionality in C#, you need to Reflect the object and get the methods, properties, or fields by their access modifiers.

You'd do something like:

var tempMethods = typeof(myClass).GetMethods();
var tempFields = typeof(myClass).GetFields();
var tempProperties = typeof(myClass).GetProperties();

then iterate through and throw them into your dynamic object:

using System;
using System.Collections.Generic;
using System.Dynamic;

namespace myApp
{
    public class myClass
    {
        public string myProp { get; set; }
        public string myField;
        public string myFunction()
        {
            return "";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var fields = typeof(myClass).GetFields();
            dynamic EO = new ExpandoObject();
            foreach (int i = 0; i < fields.Length; i++)
            {
                AddProperty(EO, "Language", "lang" + i);
                Console.Write(EO.Language);
            }
        }

        public static void AddProperty(ExpandoObject expando, string propertyName, object propertyValue)
        {
            // ExpandoObject supports IDictionary so we can extend it like this
            var expandoDict = expando as IDictionary<string, object>;
            if (expandoDict.ContainsKey(propertyName))
                expandoDict[propertyName] = propertyValue;
            else
                expandoDict.Add(propertyName, propertyValue);
        }
    }
} 

https://www.oreilly.com/learning/building-c-objects-dynamically

Comments

-1

I came here looking for the c# range operator as in numbers[ 1 .. 4]

int[] numbers = new[] { 0, 10, 20, 30, 40, 50 };
int start = 1, amountToTake = 3;
int[] subset = numbers[start..(start + amountToTake)];  // contains 10, 20, 30
numbers[1 .. 4] // returns 10, 20, 30

In this looks like this

Linqpad range operator

Comments

-6

you can also do the following

    var a  = new List<int>(new int[]{1,2,3}){5};
    Console.WriteLine(a.Count);

will print 4

if you want to achieve initialization of lists or arrays with both an accompanying enumerable and parameters

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.