Closed ufcpp closed 1 year ago
「body 省略」に class C;
構文が採用されたことで、「file-scoped クラス定義」の望みは絶たれた。
↑ file-scoped namespace みたいなのをクラスでもやりたいという話。
すでに record ではできる望みなかったので、それが正式に class/struct でも同様に。
空派生の類でも有用。
interface IA;
interface IB;
interface IC;
interface IAll : IA, IB, IC; // {} 書かない
初期化子とコンストラクターの実行順
class Base(int x) { }
class A(int x) : Base(x)
{
public int X { get; } = x; // これ、 base(x) 呼びよりも前になる
}
class B : Base
{
public int X { get; }
public B(int x) : base(x)
{
X = x;
}
}
そういやプライマリコンストラクターの有無関係なく、「派生側 初期化子 → 基底側 初期化子 → 基底側 ctor → 派生側 ctor」順だという話、うちのサイトにはまだ書いてない?
特に、「親クラスから virtual method 呼びたい」とかの時に「初期化子の方が先」仕様助かることが。
_ = new Derived("Hello");
class Base
{
public Base() => Print(); // 何も表示されない。 Name が未初期化(null)。
public virtual void Print() { }
}
class Derived : Base
{
public string Name { get; }
public Derived(string name) => Name = name;
public override void Print() => Console.WriteLine(Name);
}
_ = new Derived("Hello");
class Base
{
public Base() => Print(); // Name 初期化済み。
public virtual void Print() { }
}
class Derived(string name) : Base
{
public string Name { get; } = name;
public override void Print() => Console.WriteLine(Name);
}
2重バッキングフィールド問題
https://github.com/ufcpp-live/UfcppLiveAgenda/issues/69#issuecomment-1475253019 https://github.com/ufcpp-live/UfcppLiveAgenda/issues/69#issuecomment-1475254939
あと、「Warn on shadowing by a member from base」
CS9109 Cannot use ref, out, or in primary constructor parameter 'x' inside an instance member
ref struct A([UnscopedRef] ref int x)
{
[UnscopedRef]
public ref int X => ref x; // ダメなんだ…
}
↓これOKなのに。
ref struct A(ref int x)
{
ref int _x = ref x;
public ref int X => ref _x;
}
method 指定でプライマリコンストラクターに属性つけれるように。
[method:A]
class C(int x);
class AAttribute : Attribute;
C# 12 の「普通のクラスにプライマリコンストラクター持てるようになった」と同時に作業してたけど、レコード型に対してもできるようになってるはず。
class C([field:A] int x) // これはダメ。「not valid」警告。x をキャプチャしてもそれはフィールド扱いではない。
{
public int X => x;
}
class AAttribute : Attribute;
レコード型のは行ける。プロパティのバッキングフィールド。
既存記事、実行順の話
https://ufcpp.net/study/csharp/oo_construct.html に
ちなみに、初期化子とコンストラクターの実行順序は、 変数初期化子 → コンストラクター初期化子 → コンストラクター本体の順になります。 また、変数初期化子は、メンバーの宣言順と同じ順序で呼び出されます。
セクション切ってもいいかも。
csharplang 2691
https://ufcpp.net/study/csharp/oo_construct.html に追記? 関連: https://ufcpp.net/study/csharp/datatype/record/
class C;
は source generator で「属性1個だけつけて本体はコード生成する」シナリオで有用。 関連: https://ufcpp.net/study/csharp/misc/analyzer-generator/