Closed ufcpp closed 8 months ago
コレクションのページ、「データ型」(タプルとかパターンマッチとかあるとこ)にあってもいいかも。
target-typed
IEnumerable<(int x, (string[] y, double z)[])> x =
[
(1, [
(["a"], 1.1),
(["a", "ab"], default),
(["a", "ab", new('a', 1)], new()),
]),
(2, [
(["b"], 2.1),
(["b", "bc"], default),
(["b", "bc", new('b', 2)], new()),
]),
];
fixer ある。
new[]{}
とか new List<T> {}
以外に、Concat とか Append も対応してた。
int[] a = [1, 2, 3];
int[] b = [4, 5];
int[] x = a.Concat(b).ToArray();
// → int[] x = [..a, ..b];
int[] y = a.Append(4).ToArray();
// → int[] x = [..a, 4];
int[] z = a.Concat(b).Append(6).ToArray();
// → int[] x = [..a, ..b, 6];
natural type がない(var 相手に使えない)のは一時的な対処(12時点では決めきれなかった)で、13では入る予定。
↓今ダメらしい。行けるようにする PR あるっぽい。
↓ VS 17.8p3 / .NET 8 RC 2 から。
[X([1, 2])]
class A;
class XAttribute : Attribute
{
public XAttribute(int[] values) { }
}
オーバーロード解決がらみ:
A.M1([]);
//A.M2([]);
A.M3([]);
A.M4([]);
//A.M5([]);
A.M6([]);
A.M7([]);
A.M8([]);
A.M9([]);
//B.M(new[] { 1 }); // int[] になっちゃうので、float[] への変換できずにエラー。
B.M([1]); // target-typed なので float[] になる。
new A().M([]); // インスタンス優先。 IEnumerable<int> の方。
new A().M1([]); // 被りがなければ一応拡張メソッド側も見てもらえる。
//[].N(); // こういう拡張メソッド呼びは(今は)無理。
// [].AsList() とか [].AsSpan() とかやりたいという話はある。C# 13 目標。
class B
{
public static void M(float[] _) { }
}
class A
{
public static void M1(int[] _) { } // 具象型優先
public static void M1(IEnumerable<int> _) { }
public static void M2(int[] _) { } // ambiguous
public static void M2(List<int> _) { }
public static void M3(IList<int> _) { }
public static void M3(List<int> _) { } // 具象型優先
public static void M4(IList<int> _) { } // 具象型に近い方優先
public static void M4(IEnumerable<int> _) { }
public static void M5(IList<int> _) { } // ambiguous
public static void M5(IReadOnlyList<int> _) { }
public static void M6(int[] _) { } // 普通は具象型優先。
public static void M6(Span<int> _) { } // こっちが優先される(コレクション式のみの特殊動作)
public static void M7(IList<int> _) { } // 普通は 派生 > 型変換
public static void M7(Span<int> _) { } // こっちが優先される(コレクション式のみの特殊動作)
public static void M8(IReadOnlyList<int> _) { }
public static void M8(ReadOnlySpan<int> _) { } // こっちが優先される(コレクション式のみの特殊動作)
public static void M9(Span<int> _) { } // たぶん GA までに変わる。今こっち。GAでROSの方
public static void M9(ReadOnlySpan<int> _) { }
public void M(IEnumerable<int> _) { }
}
static class AEx
{
public static void M(this A _, Span<int> _1) { }
public static void M1(this A _, Span<int> _1) { }
public static void N(this int[] _) { }
}
↓がそこそこいいパフォーマンスになるはず。
static void X1(IEnumerable<int>? a)
{
// ここ、target-type が IEnumerable なので、 [] は Empty 最適化かかる予定
foreach (var x in a ?? [])
{
Console.WriteLine(x);
}
}
null coalescing foreach 代わり。
内部的に InlineArray 使ったり、ReadOnlyArray (コンパイラー生成)使ったり。 仕様上、実装変えるのは認められてるんで今後もっと効率のいい実装があれば変更の可能性あり。
[]
は Array.Empty になったりもする。
計画上残ってるけど C# 13 行き:
var x = [1,2,3];
不可 → natural type を何にするかで迷ってる
IList<T>
とか IEnumerable<T>
とか、List<T>
が実装してるインターフェイスに対しては使える(List<T>
になる)[1,2,3].AsList()
みたいな拡張メソッド target-type 選択 → 単純にアイディア出たのが遅すぎて間に合わない["key": value]
。 → これは12予定になってるかちょっと自信ない。元から13かもnatural type 迷うのは、
// natural type 何にすべき?
var array = [1, 2, 3];
array.Add(4); // この需要はある = List?
foreach (var x in [1,2,3,4,5,]) // アロケーションかかってほしくない = Span?
{
}
non-generic の、12時点↓はダメ。
using System.Collections;
ICollection c = ["a", 2, null];
CollectionBuilder 例えば ImmutableArray とか。 インターフェイスにもつけれる。IImmutableList とか。
CollectionBuilder → コレクション初期化子が使える型はそれと同じ lowering。 配列と、配列が実装してるインターフェイス、Span/ReadOnlySpan は特別扱いしてそう。
実装例:
using System.Collections;
using System.Runtime.CompilerServices;
MyArray<int> array = [1, 2, 3];
foreach (var x in array) Console.WriteLine(x);
[CollectionBuilder(typeof(MyArray), nameof(MyArray.Create))]
struct MyArray<T> : IEnumerable<T>
{
private readonly T[] _array;
public MyArray(T[] items) => _array = items;
public MyArray(IEnumerable<T> items) : this(items.ToArray()) { }
public MyArray(ReadOnlySpan<T> items) : this(items.ToArray()) { }
public IEnumerator<T> GetEnumerator() => _array.AsEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _array.GetEnumerator();
public MyArray<T> Add(T item) => new(_array.Append(item));
}
class MyArray
{
public static MyArray<T> Create<T>(ReadOnlySpan<T> items) => new(items);
}
{}
と new(){}
と []
// 配列に関しては {} と [] 同じ。
int[] a1 = { 1, 2, 3 };
int[] a2 = [ 1, 2, 3 ];
List<int> l1 = { 1, 2, 3 }; // ダメ
List<int> l2 = [1, 2, 3]; // OK
var x1 = new X
{
A = { 1 }, // var x1 = new(); x1.A.Add(1); 扱い(ぬるぽる)。
// こっちは init なくても平気。
B = new() { 1 }, // アロケーション2重にかかるのいや
B = { 1 }, // なので、B みたいなケースではこっちの方がいいはず。
};
var x2 = new X
{
A = [ 1 ], // x1.A = new() { 1 } 扱い。大丈夫。
};
class X
{
public List<int> A { get; init; }
public List<int> B { get; init; } = new();
}
属性にもいける。
[A([1, 2, 3, 4, 5])]
class X;
class AAttribute(int[] _) : Attribute;
spread (..
)
concat, append に使える
..
の中身からの型推論は弱め。
byte[] x = [1, 2];
Print([.. x]); // Print<byte>(byte[])
Print([.. x, 3]); // Print<int>(int[])
static void Print<T>(T[] args) { }
[..a, x, ..b]
みたいなの書いたとき、a
, b
の列挙の評価順序の保証ないみたい。
a
, x
, b
自体なこの順で評価されるけど、中身の列挙はその後になるかもしれない(遅延評価の時に挙動に保証ない)。
書くかどうかは悩ましいけど、null 条件演算子との区別。
bool b = true;
int[] x = [1];
int? y = x?[0];
int[] z = b ? [0] : [1];
コンパイラーが頑張って :
を先読みしてるみたい。
Dictionary 式が入ったとき大丈夫かどうか。
https://ufcpp.net/study/csharp/datatype/collection-expression/
あとは既存記事からのリンクと、 C# 12 の新機能ページの概要。
前にブログ書いたやつ https://ufcpp.net/blog/2023/1/collection-literal/ ディクショナリは13行き。
https://ufcpp.net/study/csharp/st_array.html 配列のところに「C# 12でこういう風にも書けるように」みたいな話足す。 「どっち使った方がいい」みたいな質問は出そう。
配列の後ろに新ページで「コレクション」とか要るかも。
もしくは、タプルの話を含めて「初期化」系のページ要るかも。
https://ufcpp.net/study/csharp/datatype/patterns/ パターンマッチングのコレクションパターンのところに「対となる構文」紹介でリンク。
コレクション式は target-typed という話、 https://ufcpp.net/blog/2022/11/covariantarrayincident/ 配列の共変性の事故は防げるはず。