Skip to content

Instantly share code, notes, and snippets.

@rent-a-developer
Created March 6, 2026 00:58
Show Gist options
  • Select an option

  • Save rent-a-developer/fe62142908ca1c96a9a646f722d46612 to your computer and use it in GitHub Desktop.

Select an option

Save rent-a-developer/fe62142908ca1c96a9a646f722d46612 to your computer and use it in GitHub Desktop.
C# params: T[] vs. IEnumerable<T> vs. ReadOnlySpan<T>
using System;
using System.Collections.Generic;
class Program
{
static void CallArray() => TakesArray(1, 2, 3);
static void CallEnumerable() => TakesEnumerable(1, 2, 3);
static void CallSpan() => TakesSpan(1, 2, 3);
static void TakesArray(params Object[] args) => _ = 0;
static void TakesEnumerable(params IEnumerable<Object> args) => _ = 0;
static void TakesSpan(params ReadOnlySpan<Object> args) => _ = 0;
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue | DebuggableAttribute.DebuggingModes.DisableOptimizations)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
[CompilerGenerated]
private readonly T[] _items;
int ICollection.Count
{
get
{
return _items.Length;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
object ICollection.SyncRoot
{
[return: Nullable(1)]
get
{
return this;
}
}
object IList.this[int index]
{
[return: Nullable(2)]
get
{
return _items[index];
}
[param: Nullable(2)]
set
{
throw new NotSupportedException();
}
}
bool IList.IsFixedSize
{
get
{
return true;
}
}
bool IList.IsReadOnly
{
get
{
return true;
}
}
int IReadOnlyCollection<T>.Count
{
get
{
return _items.Length;
}
}
T IReadOnlyList<T>.this[int index]
{
get
{
return _items[index];
}
}
int ICollection<T>.Count
{
get
{
return _items.Length;
}
}
bool ICollection<T>.IsReadOnly
{
get
{
return true;
}
}
T IList<T>.this[int index]
{
get
{
return _items[index];
}
set
{
throw new NotSupportedException();
}
}
public <>z__ReadOnlyArray(T[] items)
{
_items = items;
}
[return: Nullable(1)]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_items).GetEnumerator();
}
void ICollection.CopyTo([Nullable(1)] Array array, int index)
{
((ICollection)_items).CopyTo(array, index);
}
int IList.Add([Nullable(2)] object value)
{
throw new NotSupportedException();
}
void IList.Clear()
{
throw new NotSupportedException();
}
bool IList.Contains([Nullable(2)] object value)
{
return ((IList)_items).Contains(value);
}
int IList.IndexOf([Nullable(2)] object value)
{
return ((IList)_items).IndexOf(value);
}
void IList.Insert(int index, [Nullable(2)] object value)
{
throw new NotSupportedException();
}
void IList.Remove([Nullable(2)] object value)
{
throw new NotSupportedException();
}
void IList.RemoveAt(int index)
{
throw new NotSupportedException();
}
[return: Nullable(new byte[] { 1, 0 })]
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)_items).GetEnumerator();
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException();
}
void ICollection<T>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<T>.Contains(T item)
{
return ((ICollection<T>)_items).Contains(item);
}
void ICollection<T>.CopyTo([Nullable(new byte[] { 1, 0 })] T[] array, int arrayIndex)
{
((ICollection<T>)_items).CopyTo(array, arrayIndex);
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
int IList<T>.IndexOf(T item)
{
return ((IList<T>)_items).IndexOf(item);
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
}
[NullableContext(1)]
[Nullable(0)]
internal class Program
{
private static void CallArray()
{
object[] array = new object[3];
array[0] = 1;
array[1] = 2;
array[2] = 3;
TakesArray(array);
}
private static void CallEnumerable()
{
object[] array = new object[3];
array[0] = 1;
array[1] = 2;
array[2] = 3;
TakesEnumerable(new <>z__ReadOnlyArray<object>(array));
}
private static void CallSpan()
{
InlineArray3<object> buffer = default(InlineArray3<object>);
<PrivateImplementationDetails>.InlineArrayElementRef<InlineArray3<object>, object>(ref buffer, 0) = 1;
<PrivateImplementationDetails>.InlineArrayElementRef<InlineArray3<object>, object>(ref buffer, 1) = 2;
<PrivateImplementationDetails>.InlineArrayElementRef<InlineArray3<object>, object>(ref buffer, 2) = 3;
TakesSpan(<PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<InlineArray3<object>, object>(ref buffer, 3));
}
private static void TakesArray(params object[] args)
{
}
private static void TakesEnumerable([ParamCollection] IEnumerable<object> args)
{
}
private static void TakesSpan([ParamCollection][ScopedRef][Nullable(new byte[] { 0, 1 })] ReadOnlySpan<object> args)
{
}
}
[CompilerGenerated]
internal sealed class <PrivateImplementationDetails>
{
internal static ReadOnlySpan<TElement> InlineArrayAsReadOnlySpan<TBuffer, TElement>([In][IsReadOnly] ref TBuffer buffer, int length)
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<TBuffer, TElement>(ref Unsafe.AsRef(ref buffer)), length);
}
internal static ref TElement InlineArrayElementRef<TBuffer, TElement>(ref TBuffer buffer, int index)
{
return ref Unsafe.Add(ref Unsafe.As<TBuffer, TElement>(ref buffer), index);
}
}
@rent-a-developer
Copy link
Author

benchmark

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment