はじめに
C#12(.NET8)
からコレクション式というものが導入されました。
int[] array = [1, 2, 3]; List<int> list = [1, 2, 3]; Span<int> span = [1, 2, 3]; ReadOnlySpan<int> ros = [1, 2, 3]; ImmutableArray<int> immutable = [1, 2, 3];
[1, 2,3]
のようにして配列などのコレクションを初期化できる機能ですね。
コレクション式を使用して、共通のコレクション値を作成できます。 コレクション式は、評価時に、さまざまなコレクション型に割り当てることができる簡潔な構文です。 コレクション式には、[ と ] の括弧の間の一連の要素が含まれます。
コレクション式 (コレクション リテラル) - C# reference | Microsoft Learn
今回はコレクション式を自身が定義した型も対応できるようにする方法について紹介したいと思います。
概要
CollectionBuilderAttribute
を用いることで実現できます。
learn.microsoft.com
namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class CollectionBuilderAttribute : Attribute { /// <summary>Initialize the attribute to refer to the <paramref name="methodName"/> method on the <paramref name="builderType"/> type.</summary> /// <param name="builderType">The type of the builder to use to construct the collection.</param> /// <param name="methodName">The name of the method on the builder to use to construct the collection.</param> /// <remarks> /// <paramref name="methodName"/> must refer to a static method that accepts a single parameter of /// type <see cref="ReadOnlySpan{T}"/> and returns an instance of the collection being built containing /// a copy of the data from that span. In future releases of .NET, additional patterns may be supported. /// </remarks> public CollectionBuilderAttribute(Type builderType, string methodName) { BuilderType = builderType; MethodName = methodName; } /// <summary>Gets the type of the builder to use to construct the collection.</summary> public Type BuilderType { get; } /// <summary>Gets the name of the method on the builder to use to construct the collection.</summary> /// <remarks>This should match the metadata name of the target method. For example, this might be ".ctor" if targeting the type's constructor.</remarks> public string MethodName { get; } } }
使い方としてはclass
やstruct
・interface
に対して[CollectionBuilder]
を付与し、第一引数builderType
に対象の型を、第二引数methodName
にコレクションを構築するためのメソッドを渡します。
// Sampleという型に対して、構築するためにCreateメソッドを利用する [CollectionBuilder(typeof(Sample), nameof(Create))] public class Sample : IEnumerable<int> { private readonly int[] _values; public Sample(IEnumerable<int> items) { _values = items.ToArray(); } // IEnumerable<int>により実装したメソッド public IEnumerator<int> GetEnumerator() => _values.AsEnumerable().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _values.GetEnumerator(); // CollectionBuilderのために追加したメソッド // staticメソッド + ReadOnlySpan<T>を引数に受け取る + 対象の型を返す public static Sample Create(ReadOnlySpan<int> items) { return new Sample(items.ToArray()); } }
実験
Sample sample = [1, 2, 3]; // Output: 1, 2, 3 Console.WriteLine(string.Join(", ", sample));