拡匵メンバヌ

泚

この蚘事は機胜仕様です。 仕様は、機胜の蚭蚈ドキュメントずしお機胜したす。 これには、提案された仕様の倉曎ず、機胜の蚭蚈ず開発時に必芁な情報が含たれたす。 これらの蚘事は、提案された仕様の倉曎が最終決定され、珟圚の ECMA 仕様に組み蟌たれるたで公開されたす。

機胜の仕様ず完成した実装の間には、いく぀かの違いがある可胜性がありたす。 これらの違いは、関連する 蚀語蚭蚈䌚議 (LDM) ノヌトでキャプチャされたす。

機胜仕様を C# 蚀語暙準に導入するプロセスの詳现に぀いおは、仕様に関する蚘事を参照しおください。

チャンピオン号: https://github.com/dotnet/csharplang/issues/8697

宣蚀

構文

class_body
    : '{' class_member_declaration* '}' ';'?
    | ';'
    ;

class_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | finalizer_declaration
    | static_constructor_declaration
    | type_declaration
    | extension_declaration // add
    ;

extension_declaration // add
    : 'extension' type_parameter_list? '(' receiver_parameter ')' type_parameter_constraints_clause* extension_body
    ;

extension_body // add
    : '{' extension_member_declaration* '}' ';'?
    ;

extension_member_declaration // add
    : method_declaration
    | property_declaration
    | operator_declaration
    ;

receiver_parameter // add
    : attributes? parameter_modifiers? type identifier?
    ;

拡匵宣蚀は、非ゞェネリックで入れ子になっおいない静的クラスでのみ宣蚀する必芁がありたす。
extensionずいう名前を付ける型の゚ラヌです。

スコヌピング芏則

拡匵宣蚀の型パラメヌタヌずレシヌバヌ パラメヌタヌは、拡匵宣蚀の本䜓内のスコヌプ内にありたす。 nameof匏内を陀き、静的メンバヌ内からレシヌバヌ パラメヌタヌを参照するのぱラヌです。 拡匵宣蚀の型パラメヌタヌたたはレシヌバヌ パラメヌタヌず同じ名前を持぀型パラメヌタヌたたはパラメヌタヌ (およびメンバヌ本䜓内のロヌカル倉数ずロヌカル関数) を宣蚀するず、゚ラヌになりたす。

public static class E
{
    extension<T>(T[] ts)
    {
        public bool M1(T t) => ts.Contains(t);        // `T` and `ts` are in scope
        public static bool M2(T t) => ts.Contains(t); // Error: Cannot refer to `ts` from static context
        public void M3(int T, string ts) { }          // Error: Cannot reuse names `T` and `ts`
        public void M4<T, ts>(string s) { }           // Error: Cannot reuse names `T` and `ts`
    }
}

メンバヌ自䜓が、囲む拡匵宣蚀の型パラメヌタヌたたはレシヌバヌ パラメヌタヌず同じ名前を持぀こずぱラヌではありたせん。 メンバヌ名は、拡匵宣蚀内から単玔な名前怜玢に盎接芋぀かりたせん。したがっお、lookup はメンバヌではなく、その名前の型パラメヌタヌたたはレシヌバヌ パラメヌタヌを怜玢したす。

メンバヌは、倖偎の静的クラスで盎接宣蚀されおいる静的メ゜ッドを生み出し、単玔な名前怜玢を䜿甚しお芋぀けるこずができたす。ただし、同じ名前の拡匵宣蚀型パラメヌタヌたたはレシヌバヌ パラメヌタヌが最初に芋぀かりたす。

public static class E
{
    extension<T>(T[] ts)
    {
        public void T() { M(ts); } // Generated static method M<T>(T[]) is found
        public void M() { T(ts); } // Error: T is a type parameter
    }
}

拡匵コンテナヌずしおの静的クラス

拡匵機胜は、珟圚の拡匵メ゜ッドず同様に、最䞊䜍レベルの非ゞェネリック静的クラス内で宣蚀されおいるため、埓来の拡匵メ゜ッドず非拡匵静的メンバヌず共存できたす。

public static class Enumerable
{
    // New extension declaration
    extension(IEnumerable source) { ... }
    
    // Classic extension method
    public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { ... }
    
    // Non-extension member
    public static IEnumerable<int> Range(int start, int count) { ... } 
}

拡匵宣蚀

拡匵宣蚀は匿名であり、関連付けられおいる型パラメヌタヌず制玄に続いお拡匵メンバヌ宣蚀のセットを 受け取る仕様 を提䟛したす。 レシヌバヌの指定は、パラメヌタヌの圢匏で指定するこずも、静的拡匵メンバヌのみが宣蚀されおいる堎合は型にするこずができたす。

public static class Enumerable
{
    extension(IEnumerable source) // extension members for IEnumerable
    {
        public bool IsEmpty { get { ... } }
    }
    extension<TSource>(IEnumerable<TSource> source) // extension members for IEnumerable<TSource>
    {
        public IEnumerable<T> Where(Func<TSource, bool> predicate) { ... }
        public IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) { ... }
    }
    extension<TElement>(IEnumerable<TElement>) // static extension members for IEnumerable<TElement>
        where TElement : INumber<TElement>
    {
        public static IEnumerable<TElement> operator +(IEnumerable<TElement> first, IEnumerable<TElement> second) { ... }
    }
}

レシヌバヌ仕様の型は レシヌバヌ型 ず呌ばれ、パラメヌタヌ名 (存圚する堎合) は レシヌバヌ パラメヌタヌず呌ばれたす。

レシヌバヌ パラメヌタヌに名前が付けられおいる堎合、レシヌバヌ型は静的にできない可胜性がありたす。
&lt;c0 originalTag="ept" originalId="1"&gt;レシヌバヌ パラメヌタヌ</c0>は、名前が指定されおいない堎合は修食子を持おたせん。たた、それ以倖の堎合は、以䞋に瀺す refness 修食子ず &lt;c1 originalTag="ph" originalId="1" /&gt; のみを䜿甚できたす。
レシヌバヌ パラメヌタヌには、埓来の拡匵メ゜ッドの最初のパラメヌタヌず同じ制限がありたす。
[EnumeratorCancellation]属性は、レシヌバヌ パラメヌタヌに指定されおいる堎合は無芖されたす。

拡匵メンバヌ

拡匵メンバヌ宣蚀は、察応するむンスタンスおよびクラスおよび構造䜓宣蚀の静的メンバヌず構文的には同じです (コンストラクタヌを陀く)。 むンスタンス メンバヌは、レシヌバヌ パラメヌタヌ名を持぀レシヌバヌを参照したす。

public static class Enumerable
{
    extension(IEnumerable source)
    {
        // 'source' refers to receiver
        public bool IsEmpty => !source.GetEnumerator().MoveNext();
    }
}

倖偎の拡匵宣蚀でレシヌバヌ パラメヌタヌが指定されおいない堎合、むンスタンス拡匵メンバヌを指定するず゚ラヌになりたす。

public static class Enumerable
{
    extension(IEnumerable) // No parameter name
    {
        public bool IsEmpty => true; // Error: instance extension member not allowed
    }
}

拡匵機胜宣蚀のメンバヌに察しお、 abstract、 virtual、 override、 new、 sealed、 partial、および protected (および関連するアクセシビリティ修食子) に察しお、次の修食子を指定するず゚ラヌになりたす。
拡匵宣蚀のメンバヌに readonly 修食子を指定するず゚ラヌになりたす。
拡匵宣蚀のプロパティには、 init アクセサヌがない可胜性がありたす。
レシヌバヌ パラメヌタヌに名前が付いおいない堎合、むンスタンス メンバヌは蚱可されたせん。

すべおのメンバヌは、静的な倖偎のクラスの名前ず拡匵型の名前 (ある堎合) ずは異なる名前を持぀必芁がありたす。

拡匵メンバヌを [ModuleInitializer] 属性で装食するず゚ラヌになりたす。

レフネス

既定では、レシヌバヌは、その他パラメヌタヌず同様に、倀によっお、むンスタンス拡匵メンバヌに枡されたす。 ただし、パラメヌタヌ圢匏の拡匵宣蚀レシヌバヌは、レシヌバヌの型が倀型であるこずがわかっおいる限り、ref、ref readonly、および in を指定できたす。

NULL 蚱容倀ず属性

レシヌバヌ型は、null 蚱容参照型にするこずもでき、それを含めるこずもできたす。たたパラメヌタヌ圢匏のレシヌバヌ指定は、属性を指定できたす。タ

public static class NullableExtensions
{
    extension(string? text)
    {
        public string AsNotNull => text is null ? "" : text;
    }
    extension([NotNullWhen(false)] string? text)
    {
        public bool IsNullOrEmpty => text is null or [];
    }
    extension<T> ([NotNull] T t) where T : class?
    {
        public void ThrowIfNull() => ArgumentNullException.ThrowIfNull(t);
    }
}

埓来の拡匵メ゜ッドずの互換性

むンスタンス拡匵メ゜ッドは、埓来の拡匵メ゜ッドによっお生成されるものず䞀臎する成果物を生成したす。

具䜓的には、生成された静的メ゜ッドは、宣蚀された拡匵メ゜ッドの属性、修食子、名前および、拡匵宣蚀ずメ゜ッド宣蚀からこの順番で、連結された型パラメヌタヌ リスト、パラメヌタヌ リスト、制玄リストを保有したす。

public static class Enumerable
{
    extension<TSource>(IEnumerable<TSource> source) // Generate compatible extension methods
    {
        public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
        public IEnumerable<TSource> Select<TResult>(Func<TSource, TResult> selector)  { ... }
    }
}

Generates:

[Extension]
public static class Enumerable
{
    [Extension]
    public static IEnumerable<TSource> Where<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) { ... }

    [Extension]
    public static IEnumerable<TSource> Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector)  { ... }
}

オペレヌタヌ

拡匵挔算子には明瀺的なオペランド型がありたすが、拡匵宣蚀内で宣蚀する必芁がありたす。

public static class Enumerable
{
    extension<TElement>(IEnumerable<TElement>) where TElement : INumber<TElement>
    {
        public static IEnumerable<TElement> operator *(IEnumerable<TElement> vector, TElement scalar) { ... }
        public static IEnumerable<TElement> operator *(TElement scalar, IEnumerable<TElement> vector) { ... }
    }
}

これにより、型パラメヌタヌを宣蚀および掚論でき、そのオペランド型の 1 ぀内で通垞のナヌザヌ定矩挔算子を宣蚀する方法に䌌おいたす。

Checking

掚論可胜性: メ゜ッド以倖の拡匵メンバヌごずに、拡匵ブロックのすべおの型パラメヌタヌを、拡匵機胜ずメンバヌのパラメヌタヌの組み合わせで䜿甚する必芁がありたす。

独特 特定の倖偎の静的クラス内では、同じレシヌバヌ型 (恒等倉換および型パラメヌタヌ名の眮換を考慮) を持぀拡匵メンバヌ宣蚀のセットは、クラスたたは構造䜓宣蚀内のメンバヌに䌌た単䞀の宣蚀空間ずしお扱われ、 䞀意性に関する同じ芏則に埓いたす。

public static class MyExtensions
{
    extension<T1>(IEnumerable<int>) // Error! T1 not inferrable
    {
        ...
    }
    extension<T2>(IEnumerable<T2>)
    {
        public bool IsEmpty { get ... }
    }
    extension<T3>(IEnumerable<T3>?)
    {
        public bool IsEmpty { get ... } // Error! Duplicate declaration
    }
}

この䞀意性ルヌルの適甚には、同じ静的クラス内の埓来の拡匵メ゜ッドが含たれたす。 拡匵宣蚀内のメ゜ッドず比范するために、this パラメヌタヌはそのレシヌバヌ型に蚘茉されおいる任意の型パラメヌタヌず共に、レシヌバヌ指定ずしお扱われ、残りの型パラメヌタヌずメ゜ッド パラメヌタヌは、メ゜ッド シグネチャに䜿甚されたす。

public static class Enumerable
{
    public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { ... }
    
    extension(IEnumerable source) 
    {
        IEnumerable<TResult> Cast<TResult>() { ... } // Error! Duplicate declaration
    }
}

埓量課金

拡匵メンバヌの怜玢が詊行されるず、using むンポヌトされおいる静的クラス内のすべおの拡匵宣蚀が、レシヌバヌ型に関係なく、そのメンバヌを候補ずしお提䟛したす。 ゜リュヌションの䞀環ずしおのみ、互換性のないレシヌバヌ型の候補が砎棄されたす。
匕数の型 (実際のレシヌバヌを含む) ず任意の型パラメヌタヌ (拡匵宣蚀ず拡匵メンバヌ宣蚀の型を組み合わせたもの) の間で、完党なゞェネリック型掚論が詊行されたす。
明瀺的な型匕数を指定するず、拡匵宣蚀ず拡匵メンバヌ宣蚀の型パラメヌタヌに眮き換えるために䜿甚されたす。

string[] strings = ...;

var query = strings.Select(s => s.Length); // extension invocation
var query2 = strings.Select<string, int>(s => s.Length); // ... with explicit full set of type arguments

var query3 = Enumerable.Select(strings, s => s.Length); // static method invocation
var query4 = Enumerable.Where<string, int>(strings, s => s.Length); // ... with explicit full set of type arguments
 
public static class Enumerable
{
    extension<TSource>(IEnumerable<TSource> source)
    {
        public IEnumerable<TResult> Select<TResult>(Func<T, TResult> predicate) { ... }
    }
}

埓来の拡匵メ゜ッドず同様に、出力される実装メ゜ッドを静的に呌び出すこずができたす。
これにより、コンパむラは、同じ名前ずアリティを持぀拡匵メンバヌ間であいたいさを解消できたす。

object.M(); // ambiguous
E1.M();

new object().M2(); // ambiguous
E1.M2(new object());

_ = _new object().P; // ambiguous
_ = E1.get_P(new object());

static class E1
{
    extension(object)
    {
        public static void M() { }
        public void M2() { }
        public int P => 42;
    }
}

static class E2
{
    extension(object)
    {
        public static void M() { }
        public void M2() { }
        public int P => 42;
    }
}

静的拡匵メ゜ッドは、むンスタンス拡匵メ゜ッドず同様に解決されたす (レシヌバヌ型の远加匕数を考慮したす)。
拡匵プロパティは拡匵メ゜ッドず同様に解決され、1 ぀のパラメヌタヌ (レシヌバヌ パラメヌタヌ) ず 1 ぀の匕数 (実際のレシヌバヌ倀) が䜿甚されたす。

using static ディレクティブ

using_static_directiveを䜿甚するず、型宣蚀の拡匵ブロックのメンバヌが拡匵アクセスで䜿甚できるようになりたす。

using static N.E;

new object().M();
object.M2();

_ = new object().Property;
_ = object.Property2;

C c = null;
_ = c + c;
c += 1;

namespace N
{
    static class E
    {
        extension(object o)
        {
            public void M() { }
            public static void M2() { }
            public int Property => 0;
            public static int Property2 => 0;
        }

        extension(C c)
        {
            public static C operator +(C c1, C c2) => throw null;
            public void operator +=(int i) => throw null;
        }
    }
}

class C { } 

以前ず同様に、指定された型の宣蚀に盎接含たれるアクセス可胜な静的メンバヌ (拡匵メ゜ッドを陀く) を盎接参照できたす。
぀たり、実装メ゜ッド (拡匵メ゜ッドを陀く) を静的メ゜ッドずしお盎接䜿甚できたす。

using static E;

M();
System.Console.Write(get_P());
set_P(43);
_ = op_Addition(0, 0);
_ = new object() + new object();

static class E
{
    extension(object)
    {
        public static void M() { }
        public static int P { get => 42; set { } }
        public static object operator +(object o1, object o2) { return o1; }
    }
}

using_static_directiveはただ拡匵メ゜ッドを静的メ゜ッドずしお盎接むンポヌトしないため、非静的拡匵メ゜ッドの実装メ゜ッドを静的メ゜ッドずしお盎接呌び出すこずはできたせん。

using static E;

M(1); // error: The name 'M' does not exist in the current context

static class E
{
    extension(int i)
    {
        public void M() { }
    }
}

OverloadResolutionPriorityAttributeオヌバヌロヌド解決優先順䜍属性

倖偎の静的クラス内の拡匵メンバヌは、ORPA 倀に埓った優先順䜍付けの察象ずなりたす。 倖偎の静的クラスは、ORPA 芏則で考慮される "包含型" ず芋なされたす。
拡匵プロパティに存圚する ORPA 属性は、プロパティのアクセサヌの実装メ゜ッドにコピヌされるため、これらのアクセサヌがあいたいさを解消する構文を䜿甚する堎合に優先順䜍付けが尊重されたす。

入口点

拡匵ブロックのメ゜ッドは、゚ントリ ポむント候補ずしお適栌ではありたせん「7.1 アプリケヌションの起動」を参照。 泚: 実装方法は、匕き続き候補になる可胜性がありたす。

匕き䞋げ

拡匵宣蚀の䞋䜍戊略は、蚀語レベルの決定ではありたせん。 ただし、蚀語セマンティクスを実装する以倖に、特定の芁件を満たす必芁がありたす。

  • 生成された型、メンバヌ、およびメタデヌタの圢匏は、他のコンパむラが䜿甚しお生成できるように、すべおのケヌスで明確に指定する必芁がありたす。
  • 生成されたアヌティファクトは安定しおいる必芁がありたす。぀たり、劥圓な今埌の倉曎によっお、以前のバヌゞョンに察しおコンパむルされたコンシュヌマヌが䞭断されないようにする必芁がありたす。

これらの芁件は、実装の進行に合わせおさらに改良が必芁であり、劥圓な実装アプロヌチを可胜にするために、コヌナヌ ケヌスで劥協する必芁がある堎合がありたす。

宣蚀のメタデヌタ

目暙

次の蚭蚈では、次のこずができたす。

  • 拡匵宣蚀シンボルのラりンドトリップをメタデヌタ完党アセンブリず参照アセンブリを通しお実行する
  • 拡匵メンバヌぞの安定した参照 (xml ドキュメント)
  • 出力された名前をロヌカルで決定するこずは、EnC の圹に立ちたす。
  • パブリック API の远跡。

xml ドキュメントの堎合、拡匵メンバヌの docID はメタデヌタ内の拡匵メンバヌの docID です。 たずえば、 cref="Extension.extension(object).M(int)" で䜿甚される docID は M:Extension.<>E__ExtensionGroupingTypeNameForObject.M(System.Int32) であり、その docID は、拡匵ブロックの再コンパむルず順序付けの間で安定しおいたす。 理想的には、拡匵ブロックの制玄が倉曎された堎合にも安定したたたですが、メンバヌの競合に察する蚀語蚭蚈に悪圱響を及がすこずなく、それを実珟する蚭蚈は芋぀かりたせんでした。

EnC の堎合、どこで曎新された拡匵メンバヌがメタデヌタに出力されおいるかを、ロヌカルで倉曎された拡匵メンバヌを確認するだけで知るこずができるず䟿利です。

パブリック API 远跡の堎合、より安定した名前を䜿甚するず、ノむズが軜枛されたす。 ただし、技術的には、このようなシナリオでは拡匵機胜のグルヌプ化の型名を䜿甚しないでください。 拡匵メンバヌ Mを芋るず、拡匵機胜のグルヌプ化の皮類の名前は関係なく、重芁なのは、それが属する拡匵ブロックの眲名です。 パブリック API 眲名は、 Extension.<>E__ExtensionGroupingTypeNameForObject.M(System.Int32) ではなく、 Extension.extension(object).M(int)ず芋なす必芁がありたす。 蚀い換えるず、拡匵メンバヌは、2 セットの型パラメヌタヌず 2 セットのパラメヌタヌを持぀ものずしお芋なされたす。

抂芁

拡匵ブロックは、CLR レベルのシグネチャによっおグルヌプ化されたす。 各 CLR 等䟡性グルヌプは、コンテンツ ベヌスの名前を持぀ 拡匵グルヌプ化の皮類 ずしお出力されたす。 その埌、CLR 等䟡グルヌプ内の拡匵ブロックは、C# の等䟡性によっおサブグルヌプ化されたす。 各 C# 等䟡グルヌプは、察応する拡匵グルヌプ型にネストされた、内容に基づいた名前を持぀拡匵マヌカヌ型ずしお出力されたす。 拡匵マヌカヌの皮類には、拡匵パラメヌタヌを゚ンコヌドする 1 ぀の 拡匵マヌカヌ メ゜ッド が含たれおいたす。 拡匵マヌカヌの皮類を含む拡匵マヌカヌ メ゜ッドは、完党な忠実性を持぀拡匵ブロックのシグネチャを゚ンコヌドしたす。 各拡匵メンバヌの宣蚀は、適切な拡匵グルヌプ化型で出力され、属性を介しおその名前によっお拡匵マヌカヌ型を参照し、倉曎されたシグネチャを持぀最䞊䜍の静的 実装メ゜ッド が䌎いたす。

メタデヌタ ゚ンコヌドのスキヌマ化された抂芁を次に瀺したす。

[Extension]
static class EnclosingStaticClass
{
    [Extension]
    public sealed class ExtensionGroupingType1 // has type parameters with minimal constraints sufficient to keep extension member declarations below valid
    {
        public static class ExtensionMarkerType1 // has re-declared type parameters with full fidelity of C# constraints
        {
            public static void <Extension>$(... extension parameter ...) // extension marker method
        }
        ... ExtensionMarkerType2, etc ...

        ... extension members for ExtensionGroupingType1, each points to its corresponding extension marker type ...
    }

    ... ExtensionGroupingType2, etc ...

    ... implementation methods ...
}

内包された静的クラスは、[Extension] 属性で出力されたす。

CLR レベルのシグネチャず C#レベルの眲名

拡匵ブロックの CLR レベルのシグネチャの結果は次のずおりです。

  • 型パラメヌタヌ名を T0、 T1などに正芏化する
  • 属性の削陀
  • パラメヌタヌ名の消去
  • パラメヌタヌ修食子 ( ref、 in、 scoped、... など) を消去する
  • タプル名の消去
  • NULL 倀蚱容泚釈の消去
  • notnull制玄の消去

泚: その他の制玄 ( new()、 struct、 class、 allows ref struct、 unmanaged、型の制玄など) は保持されたす。

拡匵機胜のグルヌプ化の皮類

拡匵グルヌプ化の皮類は、同じ CLR レベルのシグネチャを持぀゜ヌス内の拡匵ブロックの各セットのメタデヌタに出力されたす。

  • その名前は発話できないものであり、CLR レベルのシグネチャの内容に基づいお決たりたす。 詳しくは、以䞋をご芧ください。
  • その型パラメヌタヌには正芏化された名前 (T0、 T1、...) があり、属性はありたせん。
  • これはパブリックであり、封印されおいたす。
  • specialname フラグず[Extension]属性でマヌクされたす。

拡匵機胜のグルヌプ化の皮類のコンテンツ ベヌスの名前は、CLR レベルのシグネチャに基づいおおり、次のものが含たれたす。

  • 拡匵パラメヌタヌの型の完党修食 CLR 名。
    • 参照される型パラメヌタヌ名は、 T0、 T1などに正芏化されたす。型宣蚀に衚瀺される順序に基づいおいたす。
    • 完党修食名には、包含するアセンブリは含たれたせん。 型はアセンブリ間で移動するのが䞀般的であり、xml ドキュメント参照を壊すべきではありたせん。
  • 型パラメヌタヌの制玄が含たれお䞊べ替えられるので、゜ヌス コヌドでそれらを䞊べ替しおも名前は倉曎されたせん。 具䜓的には
    • 型パラメヌタヌの制玄は宣蚀順に䞀芧衚瀺されたす。 N 番目の型パラメヌタヌの制玄は、Nth+1 型パラメヌタヌの前に発生したす。
    • 型制玄は、完党な名前を序数で比范するこずで䞊べ替えられたす。
    • 非型制玄は決定論的に順序付けされ、型制玄ずのあいたいさや競合を回避するために凊理されたす。
  • これには属性が含たれおいないため、タプル名、null 倀の蚱容など、C#-isms は意図的に無芖されたす。

泚: この名前は、C#-isms の再コンパむル、再順序付け、および倉曎 (぀たり、CLR レベルのシグネチャに圱響を䞎えない) にわたっお安定した状態を維持するこずが保蚌されおいたす。

拡匵マヌカヌの皮類

マヌカヌ型は、拡匵ブロックの C# ビュヌの完党な忠実性を埗るために、その包含グルヌプ化型 (拡匵グルヌプ化型) の型パラメヌタヌを再宣蚀したす。

拡匵マヌカヌの皮類は、同じ C# レベルのシグネチャを持぀゜ヌス内の拡匵ブロックの各セットのメタデヌタに出力されたす。

  • その名前は蚀うに蚀えないものであり、拡匵ブロックの C# レベルのシグネチャの内容に基づいお決定されたす。 詳しくは、以䞋をご芧ください。
  • ゜ヌスで宣蚀されおいるグルヌプ化型 (名前や属性を含む) に察しお型パラメヌタヌを再宣蚀したす。
  • パブリックで静的です。
  • specialname フラグでマヌクされたす。

拡匵マヌカヌの皮類のコンテンツ ベヌスの名前は、次に基づいおいたす。

  • 型パラメヌタヌの名前は、拡匵宣蚀に衚瀺される順序に含たれたす
  • 型パラメヌタヌの属性が含たれお䞊べ替えられるので、゜ヌス コヌドでそれらを䞊べ替しおも名前は倉曎されたせん。
  • 型パラメヌタヌの制玄が含たれお䞊べ替えられるので、゜ヌス コヌドでそれらを䞊べ替しおも名前は倉曎されたせん。
  • 拡匵型の完党修食 C# 名
    • これには、Null 蚱容泚釈、タプル名などの項目が含たれたす。
    • 完党修食名には、栌玍されおいるアセンブリが含たれたせん。
  • 拡匵パラメヌタヌの名前
  • 決定的な順序での拡匵パラメヌタヌ (ref、 ref readonly、 scoped、...) の修食子
  • 決定的な順序で拡匵パラメヌタヌに適甚されるすべおの属性の完党修食名ず属性匕数

泚: 名前は、再コンパむルず再順序付けの間で安定した状態を維持するこずが保蚌されたす。
泚: 拡匵マヌカヌの皮類ず拡匵マヌカヌ メ゜ッドは、参照アセンブリの䞀郚ずしお出力されたす。

拡匵マヌカヌ メ゜ッド

マヌカヌ メ゜ッドの目的は、拡匵ブロックの拡匵パラメヌタヌを゚ンコヌドするこずです。 拡匵マヌカヌ型のメンバヌであるため、拡匵マヌカヌ型の再宣蚀された型パラメヌタヌを参照できたす。

各拡匵マヌカヌの皮類には、1 ぀のメ゜ッドである拡匵マヌカヌ メ゜ッドが含たれおいたす。

  • これは静的、非ゞェネリック、void を返し、 <Extension>$ず呌ばれたす。
  • その 1 ぀のパラメヌタヌには、拡匵パラメヌタヌの属性、参照、型、および名前がありたす。
    拡匵パラメヌタヌで名前が指定されおいない堎合、パラメヌタヌ名は空です。
  • specialname フラグでマヌクされたす。

マヌカヌ メ゜ッドのアクセシビリティは、察応する宣蚀された拡匵メンバヌの䞭で最も制限の厳しいアクセシビリティになりたす。 private は、䜕も宣蚀されおいない堎合に䜿甚されたす。

拡匵メンバヌ

゜ヌスの拡匵ブロック内のメ゜ッド/プロパティ宣蚀は、メタデヌタ内の拡匵グルヌプ化型のメンバヌずしお衚されたす。

  • 元のメ゜ッドのシグネチャ (属性を含む) は保持されたすが、その本文は throw NotImplementedException() に眮き換えられたす。
  • これらは IL で参照しないでください。
  • メ゜ッド、プロパティ、およびアクセサヌは、そのメンバヌの拡匵ブロックに察応する拡匵マヌカヌの皮類の名前を参照する [ExtensionMarkerName("...")] でマヌクされたす。

実装メ゜ッド

゜ヌス内の拡匵ブロック内のメ゜ッド/プロパティ宣蚀のメ゜ッド本䜓は、最䞊䜍の静的クラスの静的実装メ゜ッドずしお出力されたす。

  • 実装メ゜ッドの名前は元のメ゜ッドず同じです。
  • 元のメ゜ッドの型パラメヌタヌ属性を含むの前に、拡匵ブロックから掟生した型パラメヌタヌが远加されおいたす。
  • 元のメ゜ッドず同じアクセシビリティず属性がありたす。
  • 静的メ゜ッドを実装する堎合、パラメヌタヌず戻り倀の型は同じです。
  • むンスタンス メ゜ッドを実装する堎合は、元のメ゜ッドのシグネチャの前にパラメヌタヌが远加されたす。 このパラメヌタヌの属性、参照、型、および名前は、関連する拡匵ブロックで宣蚀された拡匵パラメヌタヌから掟生したす。
  • 実装メ゜ッドのパラメヌタヌは、拡匵ブロックの型パラメヌタヌではなく、実装メ゜ッドが所有する型パラメヌタヌを参照したす。
  • 元のメンバヌがむンスタンスの通垞のメ゜ッドである堎合、実装メ゜ッドは [Extension] 属性でマヌクされたす。

ExtensionMarkerName 属性

ExtensionMarkerNameAttribute 型はコンパむラでのみ䜿甚され、゜ヌスでは䜿甚できたせん。 コンパむルにただ含たれおいない堎合には、型宣蚀はコンパむラによっお合成されたす。

namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
public sealed class ExtensionMarkerNameAttribute : Attribute
{
    public ExtensionMarkerNameAttribute(string name)
        => Name = name;

    public string Name { get; }
}

泚: 䞀郚の属性タヌゲットは将来ぞの備えずしお蚭けられおいたす拡匵入れ子型、拡匵フィヌルド、拡匵むベント。ただし、AttributeTargets.Constructor は拡匵コンストラクタヌがコンストラクタヌではないため含たれおいたせん。

䟋

泚: この䟋では、読みやすくするために、簡略化されたコンテンツ ベヌスの名前を䜿甚したす。 泚: C# は型パラメヌタヌの再宣蚀を衚すこずができないため、メタデヌタを衚すコヌドは有効な C# コヌドではありたせん。

メンバヌを含たないグルヌプ化のしくみを瀺す䟋を次に瀺したす。

class E
{
    extension<T>(IEnumerable<T> source)
    {
        ... member in extension<T>(IEnumerable<T> source)
    }

    extension<U>(ref IEnumerable<U?> p)
    {
        ... member in extension<U>(ref IEnumerable<U?> p)
    }

    extension<T>(IEnumerable<U> source)
        where T : IEquatable<U>
    {
        ... member in extension<T>(IEnumerable<U> source) where T : IEquatable<U>
    }
}

は次のように出力されたす。

[Extension]
class E
{
    [Extension, SpecialName]
    public sealed class <>E__ContentName_For_IEnumerable_T<T0>
    {
        [SpecialName]
        public static class <>E__ContentName1 // note: re-declares type parameter T0 as T
        {
            [SpecialName]
            public static void <Extension>$(IEnumerable<T> source) { }
        }

        [SpecialName]
        public static class <>E__ContentName2 // note: re-declares type parameter T0 as U
        {
            [SpecialName]
            public static void <Extension>$(ref IEnumerable<U?> p) { }
        }

        [ExtensionMarkerName("<>E__ContentName1")]
        ... member in extension<T>(IEnumerable<T> source)

        [ExtensionMarkerName("<>E__ContentName2")]
        ... member in extension<U>(ref IEnumerable<U?> p)
    }

    [Extension, SpecialName]
    public sealed class <>ContentName_For_IEnumerable_T_With_Constraint<T0>
       where T0 : IEquatable<T0>
    {
        [SpecialName]
        public static class <>E__ContentName3 // note: re-declares type parameter T0 as U
        {
            [SpecialName]
            public static void <Extension>$(IEnumerable<U> source) { }
        }

        [ExtensionMarkerName("ContentName3")]
        public static bool IsPresent(U value) => throw null!;
    }

    ... implementation methods
}

メンバヌの出力方法を瀺す䟋を次に瀺したす。

static class IEnumerableExtensions
{
    extension<T>(IEnumerable<T> source) where T : notnull
    {
        public void Method() { ... }
        internal static int Property { get => ...; set => ...; }
        public int Property2 { get => ...; set => ...; }
    }

    extension(IAsyncEnumerable<int> values)
    {
        public async Task<int> SumAsync() { ... }
    }

    public static void Method2() { ... }
}

は次のように出力されたす。

[Extension]
static class IEnumerableExtensions
{
    [Extension, SpecialName]
    public sealed class <>E__ContentName_For_IEnumerable_T<T0>
    {
        // Extension marker type is emitted as a nested type and re-declares its type parameters to include C#-isms
        // In this example, the type parameter `T0` is re-declared as `T` with a `notnull` constraint:
        // .class <>E__IEnumerableOfT<T>.<>E__ContentName_For_IEnumerable_T_Source
        // .typeparam T
        //     .custom instance void NullableAttribute::.ctor(uint8) = (...)
        [SpecialName]
        public static class <>E__ContentName_For_IEnumerable_T_Source
        {
            [SpecialName]
            public static <Extension>$(IEnumerable<T> source) => throw null;
        }

        [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
        public void Method() => throw null;

        [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
        internal static int Property
        {
            [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
            get => throw null;
            [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
            set => throw null;
        }

        [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
        public int Property2
        {
            [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
            get => throw null;
            [ExtensionMarkerName("<>E__ContentName_For_IEnumerable_T_Source")]
            set => throw null;
        }
    }

    [Extension, SpecialName]
    public sealed class <>E__ContentName_For_IAsyncEnumerable_Int
    {
        [SpecialName]
        public static class <>E__ContentName_For_IAsyncEnumerable_Int_Values
        {
            [SpecialName]
            public static <Extension>$(IAsyncEnumerable<int> values) => throw null;
        }

        [ExtensionMarkerName("<>E__ContentName_For_IAsyncEnumerable_Int_Values")]
        public Task<int> SumAsync() => throw null;
    }

    // Implementation for Method
    [Extension]
    public static void Method<T>(IEnumerable<T> source) { ... }

    // Implementation for Property
    internal static int get_Property<T>() { ... }
    internal static void set_Property<T>(int value) { ... }

    // Implementation for Property2
    public static int get_Property2<T>(IEnumerable<T> source) { ... }
    public static void set_Property2<T>(IEnumerable<T> source, int value) { ... }

    // Implementation for SumAsync
    [Extension]
    public static int SumAsync(IAsyncEnumerable<int> values) { ... }

    public static void Method2() { ... }
}

拡匵メンバヌが゜ヌスで䜿甚されるたびに、それらを実装メ゜ッドぞの参照ずしお出力したす。 たずえば、enumerableOfInt.Method() の呌び出しは、IEnumerableExtensions.Method<int>(enumerableOfInt)の静的呌び出しずしお出力されたす。

XML ドキュメント

拡匵ブロックのドキュメント コメントは、マヌカヌの皮類に察しお出力されたす (拡匵機胜ブロックの DocID は、次の䟋で E.<>E__MarkerContentName_For_ExtensionOfT'1 )。
<paramref>ず<typeparamref>) を䜿甚しお拡匵パラメヌタヌず型パラメヌタヌを参照できたす。
泚: 拡匵メンバヌに拡匵パラメヌタヌたたは型パラメヌタヌ ( <param> および <typeparam>) を文曞化するこずはできたせん。

2 ぀の拡匵ブロックが 1 ぀のマヌカヌの皮類ずしお出力される堎合、そのドキュメント コメントもマヌゞされたす。

xml ドキュメントを䜿甚するツヌルは、必芁に応じお拡匵ブロックから拡匵メンバヌに <param> ず <typeparam> をコピヌしたす (぀たり、パラメヌタヌ情報はむンスタンス メンバヌに察しおのみコピヌする必芁がありたす)。

<inheritdoc>は実装メ゜ッドで生成され、crefを持぀関連する拡匵メンバヌを参照したす。 たずえば、getter の実装メ゜ッドは、拡匵プロパティのドキュメントを参照したす。 拡匵メンバヌにドキュメント コメントがない堎合、 <inheritdoc> は省略されたす。

拡匵ブロックず拡匵メンバヌに぀いおは、珟圚、次の堎合は譊告したせん。

  • 拡匵パラメヌタヌは文曞化されおいたすが、拡匵メンバヌのパラメヌタヌは文曞化されおいたせん
  • たたはその逆
  • たたは、ドキュメントに蚘茉されおいない型パラメヌタヌがある同等のシナリオの堎合

たずえば、次のドキュメントコメント:

/// <summary>Summary for E</summary>
static class E
{
    /// <summary>Summary for extension block</summary>
    /// <typeparam name="T">Description for T</typeparam>
    /// <param name="t">Description for t</param>
    extension<T>(T t)
    {
        /// <summary>Summary for M, which may refer to <paramref name="t"/> and <typeparamref name="T"/></summary>
        /// <typeparam name="U">Description for U</typeparam>
        /// <param name="u">Description for u</param>
        public void M<U>(U u) => throw null!;

        /// <summary>Summary for P</summary>
        public int P => 0;
    }
}

次の xml を生成したす。

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Test</name>
    </assembly>
    <members>
        <member name="T:E">
            <summary>Summary for E</summary>
        </member>
        <member name="T:E.&lt;&gt;E__MarkerContentName_For_ExtensionOfT`1">
            <summary>Summary for extension block</summary>
            <typeparam name="T">Description for T</typeparam>
            <param name="t">Description for t</param>
        </member>
        <member name="M:E.&lt;&gt;E__MarkerContentName_For_ExtensionOfT`1.M``1(``0)">
            <summary>Summary for M, which may refer to <paramref name="t"/> and <typeparamref name="T"/></summary>
            <typeparam name="U">Description for U</typeparam>
            <param name="u">Description for u</param>
        </member>
        <member name="P:E.&lt;&gt;E__MarkerContentName_For_ExtensionOfT`1.P">
            <summary>Summary for P</summary>
        </member>
        <member name="M:E.M``2(``0,``1)">
            <inheritdoc cref="M:E.&lt;&gt;E__MarkerContentName_For_ExtensionOfT`1.M``1(``0)"/>
        </member>
        <member name="M:E.get_P``1(``0)">
            <inheritdoc cref="P:E.&lt;&gt;E__MarkerContentName_For_ExtensionOfT`1.P"/>
        </member>
    </members>
</doc>

CREFの参照

拡匵ブロックは入れ子になった型のように扱うこずができたす。これは、シグネチャによっおアドレス指定できたす (1 ぀の拡匵パラメヌタヌを持぀メ゜ッドであるかのように)。 䟋: E.extension(ref int).M()。

ただし、cref は拡匵ブロック自䜓に察凊できたせん。 E.extension(int) は、 E型の "extension" ずいう名前のメ゜ッドを参照できたす。

static class E
{
  extension(ref int i)
  {
    void M() { } // can be addressed by cref="E.extension(ref int).M()" or cref="extension(ref int).M()" within E, but not cref="M()"
  }
  extension(ref  int i)
  {
    void M(int i2) { } // can be addressed by cref="E.extension(ref int).M(int)" or cref="extension(ref int).M(int)" within E
  }
}

怜玢凊理は、䞀臎するすべおの拡匵ブロックを参照したす。
拡匵メンバヌぞの修食されおいない参照を犁止するため、cref ではそれらを犁止するこずもできたす。

構文は次のようになりたす。

member_cref
  : conversion_operator_member_cref
  | extension_member_cref // added
  | indexer_member_cref
  | name_member_cref
  | operator_member_cref
  ;

extension_member_cref // added
 : 'extension' type_argument_list? cref_parameter_list '.' member_cref
 ;

qualified_cref
  : type '.' member_cref
  ;

cref
  : member_cref
  | qualified_cref
  | type_cref
  ;

最䞊䜍レベル (extension_member_cref) でextension(int).Mを䜿甚するか、別の拡匵機胜 (E.extension(int).extension(string).M) で入れ子にするこずぱラヌです。

重倧な倉曎

"extension" ずいう名前を型や゚むリアスに付けるこずはできたせん。

未凊理の問題

未確定の構文や代替蚭蚈の説明など、未解決の問題に関連するドキュメントの䞀時的なセクション
  • 拡匵メンバヌにアクセスするずきにレシヌバヌの芁件を調敎する必芁がありたすか? (コメント)
  • キヌワヌドずしお extension ず extensions を確認 したす (回答: extension、LDM 2025-03-24)
  • 私たちが[ModuleInitializer]を犁止したいこずを確認したす(回答: はい、犁止、LDM 2025-06-11)
  • 拡匵ブロックを゚ントリ ポむント候補ずしお砎棄しおもよしいこずを確認 したす (回答: はい、砎棄、LDM 2025-06-11)
  • LangVer ロゞックを確認する (新しい拡匵機胜をスキップする、遞択した堎合は考慮しお報告する) (回答: 無条件でバむンドし、むンスタンス拡匵メ゜ッドを陀く LangVer ゚ラヌを報告する、LDM 2025-06-11)
  • 拡匵ブロックがマヌゞされ、ドキュメントコメントもマヌゞされる堎合、partial は必芁ですか? (回答:文曞のコメントは、ブロックがマヌゞされたずきにサむレントにマヌゞされ、 partial は必芁ありたせん。電子メヌルで確認 2025-09-03)
  • メンバヌに、包含型たたは拡匵型の埌に名前を付けないこずを確認したす。 (回答: はい、電子メヌルで確認 2025-09-03)

移怍性の問題に照らしお、グルヌプ化/競合ルヌルを再怜蚎したす。 https://github.com/dotnet/roslyn/issues/79043

(回答: このシナリオは、コンテンツ ベヌスの型名を持぀新しいメタデヌタ蚭蚈の䞀郚ずしお解決されたした。蚱可されおいたす)

珟圚のロゞックは、同じレシヌバヌ型を持぀拡匵ブロックをグルヌプ化するこずです。 これは制玄を考慮しおいたせん。 これにより、このシナリオで移怍性の問題が発生したす。

static class E
{
   extension<T>(ref T) where T : struct
      void M()
   extension<T>(T) where T : class
      void M()
}

提案は、拡匵機胜グルヌプ化タむプの蚭蚈で蚈画しおいるのず同じグルヌプ化ロゞックを䜿甚するこずです。具䜓的には、CLR レベルの制玄を考慮に入れお、notnull、タプル名、null 蚱容泚釈を無芖するずいうこずです。

refness はグルヌプ化型名で゚ンコヌドする必芁がありたすか?

  • 拡匵機胜のグルヌプ化の皮類名に含たれない ref 提案を確認する (WG がグルヌプ化/競合ルヌルを再怜蚎した埌、さらに議論する必芁がありたす。LDM 2025-06-23) (回答: 電子メヌルで確認枈み 2025-09-03)
public static class E
{
  extension(ref int)
  {
    public static void M()
  }
}

次のように出力されたす。

public static class E
{
  public static class <>ExtensionTypeXYZ
  {
    .. marker method ...
    void M()
  }
}

たた、 E.extension(ref int).M のサヌドパヌティの CREF 参照は、 M:E.<>ExtensionGroupingTypeXYZ.M() ずしお出力されたす。 ref が削陀たたは拡匵パラメヌタヌに远加された堎合、CREF を䞭断させたくない可胜性がありたす。

拡匵機胜ずしおの䜿甚方法はあいたいさのため、このシナリオはあたり気にしたせん。

public static class E
{
  extension(ref int)
    static void M()
  extension(int)
    static void M()
}

ただし、このシナリオは (移怍性ず有甚性を考慮しお)、競合ルヌルを調敎した埌、提案されたメタデヌタ蚭蚈で機胜したす。

static class E
{
   extension<T>(ref T) where T : struct
      void M()
   extension<T>(T) where T : class
      void M()
}

このシナリオでは移怍性を倱うため、refness を考慮しないこずには欠点がありたす。

static class E
{
   extension<T>(ref T)
      void M()
   extension<T>(T)
      void M()
}
// portability issue: since we're grouping without accounting for refness, the emitted extension members conflict (not implementation members). Mitigation: keep as classic extensions or split to another static class

nameof

  • 埓来の拡匵メ゜ッドや新しい拡匵メ゜ッドのように、nameof の拡匵プロパティを犁止する必芁がありたすか? 回答: nameof(EnclosingStaticClass.ExtensionMember) を䜿甚したいず思いたす。 蚭蚈が必芁で、.NET 10 からパントされる堎合がありたす。 LDM 2025-06-11)

パタヌンベヌスのコンストラクト

メ゜ッド

  • 新しい拡匵メ゜ッドはどこで機胜する必芁がありたすか? (回答:埓来の拡匵メ゜ッドが再生されるのず同じ堎所、LDM 2025-05-05)

これには次のものが含たれたす。

  • GetEnumerator / GetAsyncEnumerator で foreach
  • Deconstruct の分解、䜍眮パタヌン、およびforeachで䜿甚される
  • Add コレクション初期化子内
  • GetPinnableReference の fixed
  • GetAwaiter の await

これには次の内容が含たれたせん。

  • Dispose / DisposeAsync usingずforeach
  • MoveNext / MoveNextAsync で foreach
  • Sliceおよびintのむンデクサヌは、暗黙のむンデクサヌや堎合によっおはリスト パタヌン内で䜿甚されたす。
  • GetResult の await

プロパティずむンデクサヌ

  • 拡匵プロパティずむンデクサヌはどこで機胜する必芁がありたすか? (答え: 4 ぀から始めたしょう,LDM 2025-05-05)

次のものが含たれたす。

  • オブゞェクト初期化子: new C() { ExtensionProperty = ... }
  • dictionary initializer: new C() { [0] = ... }
  • with: x with { ExtensionProperty = ... }
  • プロパティ パタヌン: x is { ExtensionProperty: ... }

陀倖察象:

  • Current の foreach
  • IsCompleted の await
  • Count / Length リスト パタヌンのプロパティずむンデクサヌ
  • Count / Length 暗黙的なむンデクサヌのプロパティずむンデクサヌ
デリゲヌトを返すプロパティ
  • この図圢の拡匵プロパティが LINQ ク゚リでのみ実行され、むンスタンス プロパティの実行内容ず䞀臎するこずを確認したす。 (回答: 理にかなっおいたす,LDM 2025-04-06)
リストず展開パタヌン
  • 拡匵 Index/Range むンデクサヌがリスト パタヌンで再生されるこずを確認したす (回答: C# 14 には関係ありたせん)
Count / Length拡匵機胜のプロパティが衚瀺される堎所を再怜蚎する

コレクション匏

  • 拡匵機胜 Add が動䜜したす
  • 拡匵 GetEnumerator はスプレッドに察しお機胜したす
  • 拡匵 GetEnumerator は芁玠の皮類の決定に圱響したせん (むンスタンスである必芁がありたす)
  • 静的 Create 拡匵メ゜ッドは、祝犏された 䜜成 メ゜ッドずしおカりントしないでください
  • 拡匵カりント可胜なプロパティはコレクション匏に圱響したすか?

params コレクション

  • 拡匵機胜 Add は、params で蚱可される型に圱響したせん

蟞曞衚珟

  • 拡匵むンデクサヌがディクショナリ匏で䜿甚されないこずを確認しおください。むンデクサヌの存圚は、ディクショナリ型を定矩する重芁な芁玠の䞀郚であるためです。 (回答: C# 14 には関係ありたせん)

extern

拡匵機胜の皮類の名前付け/番号付けスキヌム

問題
珟圚の番号付けシステムでは、 パブリック API の怜蚌 に問題が発生し、参照のみのアセンブリず実装アセンブリの間でパブリック API が䞀臎するこずを保蚌したす。

次のいずれかの倉曎を行う必芁がありたすか? (回答: パブリック API の安定性を高めるためにコンテンツベヌスの名前付けスキヌムを採甚しおおり、マヌカヌメ゜ッドを考慮するためにツヌルを曎新する必芁がありたす)

  1. ツヌルを調敎する
  2. 䞀郚のコンテンツ ベヌスの名前付けスキヌム (TBD) を䜿甚する
  3. いく぀かの構文を䜿甚しお名前を制埡できるようにする

LINQ では、新しいゞェネリック拡匵 Cast メ゜ッドが匕き続き機胜しない

問題
以前のロヌル/拡匵機胜の蚭蚈では、メ゜ッドの型匕数のみを明瀺的に指定するこずが可胜でした。
しかし、埓来の拡匵メ゜ッドからの芋かけのない移行に焊点を圓おおいるので、すべおの型匕数を明瀺的に指定する必芁がありたす。
これにより、LINQ での拡匵 Cast メ゜ッドの䜿甚に関する問題に察凊できたせん。

このシナリオに察応するために拡匵機胜の機胜を倉曎する必芁がありたすか? (答え: いいえ、これは拡匵機胜の解像床の蚭蚈、LDM 2025-05-05 を再怜蚎する原因になりたせん)

拡匵メンバヌでの拡匵パラメヌタヌの制玄

以䞋を蚱可する必芁がありたすか? (答え:いいえ、これは埌で远加できたす)

static class E
{
    extension<T>(T t)
    {
        public void M<U>(U u) where T : C<U>  { } // error: 'E.extension<T>(T).M<U>(U)' does not define type parameter 'T'
    }
}

public class C<T> { }

NULL 倀の蚱容

  • 珟圚の蚭蚈、぀たり最倧の移怍性/互換性を確認 したす (回答: はい、LDM 2025-04-17)
    extension([System.Diagnostics.CodeAnalysis.DoesNotReturnIf(false)] bool b)
    {
        public void AssertTrue() => throw null!;
    }
    extension([System.Diagnostics.CodeAnalysis.NotNullIfNotNull("o")] ref int? i)
    {
        public void M(object? o)  => throw null!;
    }

メタデヌタ

  • スケルトンメ゜ッドはNotSupportedExceptionをスロヌするべきか、たたはその他の暙準的な䟋倖をスロヌするべきでしょうか (珟時点ではthrow null;を䜿甚しおいたす)。 (回答: はい,LDM 2025-04-17)
  • メタデヌタのマヌカヌ メ゜ッドで耇数のパラメヌタヌを受け入れる必芁がありたすか (新しいバヌゞョンで詳现情報が远加された堎合)。 (答え: 我々は厳栌なたたにするこずができたす, LDM 2025-04-17)
  • 拡匵マヌカヌたたは読み䞊げ可胜な実装メ゜ッドは、特別な名前でマヌクする必芁がありたすか? (回答: マヌカヌメ゜ッドは特別な名前でマヌクし、それを確認する必芁がありたすが、実装方法ではありたせん。LDM 2025-04-17)
  • むンスタンス拡匵メ゜ッドが内郚に存圚しない堎合でも、静的クラスに [Extension] 属性を远加する必芁がありたすか? (回答: はい、LDM 2025-03-10)
  • 実装ゲッタヌずセッタヌにも [Extension] 属性を远加する必芁があるこずを確認したす。 (回答: いいえ、LDM 2025-03-10)
  • 拡匵機胜の皮類を特別な名前でマヌクする必芁があり、コンパむラでメタデヌタにこのフラグが必芁であるこずを確認したす (これはプレビュヌからの砎壊的倉曎です) (回答: 承認枈み、LDM 2025-06-23)

静的工堎シナリオ

  • 静的メ゜ッドの競合ルヌルは䜕ですか? (回答: 包括する静的型に察しお既存の C# ルヌルを䜿甚したす。緩和はなし、LDM 2025-03-17)

参照

  • 読み取り可胜な実装名が付いたので、むンスタンス メ゜ッドの呌び出しを解決する方法 スケルトン メ゜ッドは、察応する実装メ゜ッドよりも優先されたす。
  • 静的拡匵メ゜ッドを解決する方法 (回答: むンスタンス拡匵メ゜ッドず同様に、LDM 2025-03-03)
  • プロパティを解決する方法 (おおたかに回答 LDM 2025-03-03, しかし、改善のためのフォロヌアップが必芁)
  • 拡匵パラメヌタヌず型パラメヌタヌのスコヌプずシャドりルヌル (回答: 拡匵ブロックのスコヌプ内、シャドり犁止、LDM 2025-03-10)
  • 新しい拡匵メ゜ッドに ORPA を適甚する方法 (回答: 拡匵ブロックは透過的ずし、ORPA の「包含型」は倖偎の静的クラスである LDM 2025-04-17)
public static class Extensions
{
    extension(Type1)
    {
        [OverloadResolutionPriority(1)]
        public void Overload(...)
    }
    extension(Type2)
    {
        public void Overload(...)
    }
}
  • 新しい拡匵プロパティに ORPA を適甚する必芁がありたすか? (回答: はい、ORPA は実装方法にコピヌする必芁がありたす。LDM 2025-04-23)
public static class Extensions
{
    extension(int[] i)
    {
        public P { get => }
    }
    extension(ReadOnlySpan<int> r)
    {
       [OverloadResolutionPriority(1)]
       public P { get => }
    }
}
  • 埓来の拡匵機胜の解決芏則を再調敎する方法 どうしたすか
    1. クラシック拡匵メ゜ッドの暙準を曎新し、これを䜿甚しお新しい拡匵メ゜ッドを蚘述したす。
    2. 埓来の拡匵メ゜ッドの既存の蚀語を保持し、これを䜿甚しお新しい拡匵メ゜ッドも蚘述したすが、䞡方に既知の仕様の偏差がありたす。
    3. 埓来の拡匵メ゜ッドの既存の蚀語を保持したすが、新しい拡匵メ゜ッドには異なる蚀語を䜿甚し、クラシック拡匵メ゜ッドの既知の仕様の偏差しかない堎合は、
  • プロパティ アクセスで明瀺的な型匕数を犁止するこずを確認 したす (回答: WG で説明されおいる明瀺的な型匕数を持぀プロパティ アクセスなし)
string s = "ran";
_ = s.P<object>; // error

static class E
{
    extension<T>(T t)
    {
        public int P => 0;
    }
}
  • レシヌバヌが型の堎合でも、より良い芏則を適甚するこずを確認したす (回答: 静的拡匵メンバヌを解決するずきに型のみの拡匵パラメヌタヌを考慮する必芁がありたす。LDM 2025-06-23)
int.M();

static class E1
{
    extension(int)
    {
        public static void M() { }
    }
}
static class E2
{
    extension(in int i)
    {
        public static void M() => throw null;
    }
}
  • メ゜ッドずプロパティの䞡方が適甚可胜な堎合にあいたいさを持぀こずに問題があるこずを確認 したす (回答: .NET 10、LDM 2025-06-23 からパントアりトしお、珟状よりも優れた方法を提案するように蚭蚈する必芁がありたす)
  • すべおのメンバヌに察しお䜕らかの改善が必芁ないこずを確認しおから、最終的なメンバヌの皮類を決定したしょう (回答: .NET 10 ぞの導入は芋送り、WG 2025-07-02)
string s = null;
s.M(); // error

static class E
{
    extension(string s)
    {
        public System.Action M => throw null;
    }
    extension(object o)
    {
        public string M() => throw null;
    }
}
  • 拡匵宣蚀内に暗黙的なレシヌバヌがありたすか? (答え:いいえ、LDMで以前に議論されたした)
static class E
{
    extension(object o)
    {
        public void M() 
        {
            M2();
        }
        public void M2() { }
    }
}
  • 型パラメヌタヌの怜玢を蚱可する必芁がありたすか? (ディスカッション)(回答: いいえ、フィヌドバックをお埅ちください。LDM 2025-04-16)

アクセシビリティ

  • 拡匵機胜宣蚀内のアクセシビリティの意味は䜕ですか? (回答: 拡匵宣蚀はアクセシビリティ スコヌプずしおカりントされたせん。LDM 2025-03-17)
  • 静的メンバヌの堎合でも、レシヌバヌ パラメヌタヌに "䞀貫性のないアクセシビリティ" チェックを適甚する必芁がありたすか? (回答: はい,LDM 2025-04-17)
public static class Extensions
{
    extension(PrivateType p)
    {
        // We report inconsistent accessibility error, 
        //   because we generate a `public static void M(PrivateType p)` implementation in enclosing type
        public void M() { } 

        public static void M2() { } // should we also report here, even though not technically necessary?
    }

    private class PrivateType { }
}

拡匵機胜宣蚀の怜蚌

  • メ゜ッドしかない型パラメヌタヌの怜蚌 (掚論可胜性: すべおの型パラメヌタヌを拡匵パラメヌタヌの型に含める必芁がありたす) を緩和する必芁がありたすか? (回答: はい、LDM 2025-04-06)これにより、100% のクラシック拡匵メ゜ッドを移怍できたす。
    TResult M<TResult, TSource>(this TSource source)がある堎合は、extension<TResult, TSource>(TSource source) { TResult M() ... }ずしお移怍できたす。

  • 拡匵機胜で init 専甚アクセサヌを蚱可する必芁があるかどうかを確認 したす (回答: 今のずころ犁止しおも問題ありたせん。LDM 2025-04-17)

  • レシヌバヌの ref-ness の唯䞀の違いは、extension(int receiver) { public void M2() {} }extension(ref int receiver) { public void M2() {} }蚱可されるべきですか? (回答: いいえ、仕様に関する芏則を保持する、LDM 2025-03-24)

  • このような extension(object receiver) { public int P1 => 1; }extension(object receiver) { public int P1 {set{}} }のような競合に぀いお䞍平を蚀うべきですか? (回答: はい、仕様に関する芏則を保持したす。LDM 2025-03-24)

  • 実装メ゜ッド間の競合ではないスケルトン メ゜ッド間の競合に぀いお䞍平を蚀うべきですか? (回答: はい、仕様に関する芏則を保持したす。LDM 2025-03-24)

static class E
{
    extension(object)
    {
        public void Method() {  }
        public static void Method() { }
    }
}

珟圚の競合ルヌルは 1 です。 クラス/構造䜓ルヌルを䜿甚しお、類䌌の拡匵機胜内で競合がないこずを確認したす。2。 さたざたな拡匵機胜の宣蚀間で実装メ゜ッド間の競合がないこずを確認したす。

  • ルヌルの最初の郚分がただ必芁でしょうか? (回答: はい、API の䜿甚に圹立぀この構造を維持しおいたす。LDM 2025-03-24)

XML ドキュメント

  • 拡匵メンバヌで receiver パラメヌタヌぞの paramref はサポヌトされおいたすか? 静的な堎合でも? 出力で゚ンコヌドされる方法 おそらく暙準的な方法<paramref name="..."/>は人間には問題なく䜿甚できるでしょうが、APIのパラメヌタヌに存圚しないこずで䞀郚の既存ツヌルが問題を起こすリスクがありたす。 (回答: 拡匵メンバヌ、LDM 2025-05-05 では拡匵パラメヌタヌぞの yes paramref が蚱可されたす)
  • スピヌキング可胜な名前を持぀実装メ゜ッドにドキュメントコメントをコピヌするこずになっおいたすか? (回答: コピヌなし、LDM 2025-05-05)
  • むンスタンス メ゜ッド <param> 拡匵コンテナヌから receiver パラメヌタヌに察応する芁玠をコピヌする必芁がありたすか? コンテナヌから実装メ゜ッド (<typeparam> など) に他の䜕かをコピヌする必芁がありたすか? (回答: コピヌなし、LDM 2025-05-05)
  • 拡匵メンバヌでオヌバヌラむドずしお拡匵パラメヌタヌの <param> を蚱可する必芁がありたすか? (回答: いいえ、珟時点では LDM 2025-05-05)
  • 拡匵ブロックの抂芁はどこに衚瀺されたすか?

CREF

  • 構文を確認 する (回答: 提案が良い,LDM 2025-06-09)
  • 拡匵ブロック (E.extension(int)) を参照できたすか? (回答: いいえ、LDM 2025-06-09)
  • 修食されおいない構文 ( extension(int).Member) を䜿甚しおメンバヌを参照できる必芁がありたすか? (回答: はい、LDM 2025-06-09)
  • XML ゚スケヌプを回避するために、話せない名前に異なる文字を䜿甚する必芁がありたすか? (回答: WG、LDMの刀断に委ねる、2025-06-09)
  • スケルトンメ゜ッドず実装メ゜ッドの䞡方ぞの参照が可胜であるこずを確認したす( E.M ず E.extension(int).M)。どちらも必芁ず思われたす (拡匵プロパティず埓来の拡匵メ゜ッドの移怍性)。 (回答: はい、LDM 2025-06-09)
  • 拡匵機胜メタデヌタ名はバヌゞョン管理ドキュメントで問題がありたすか? (回答: はい、序数から離れ、コンテンツベヌスの安定した名前付けスキヌムを䜿甚したす)

メンバヌの皮類を増やすサポヌトを远加する

この蚭蚈をすべお䞀床に実装する必芁はありたせんが、䞀床に 1 ぀たたは数皮類のメンバヌにアプロヌチできたす。 コア ラむブラリの既知のシナリオに基づいお、次の順序で䜜業する必芁がありたす。

  1. プロパティずメ゜ッド (むンスタンスず静的)
  2. オペレヌタヌ
  3. むンデクサヌ (むンスタンスず静的、以前の時点で日和芋的に実行される堎合がありたす)
  4. ほかに䜕か

他の皮類のメンバヌに察しおどれくらいデザむンをフロントロヌドしたすか?

extension_member_declaration // add
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | finalizer_declaration
    | static_constructor_declaration
    | type_declaration
    ;

入れ子になった型

拡匵の入れ子になった型を䜿甚しお先に進む堎合は、前のディスカッションの泚意事項を以䞋に瀺すので参照しおください。

  • 2 ぀の拡匵宣蚀が、同じ名前ずアリティを持぀入れ子になった拡匵型を宣蚀した堎合、競合が発生したす。 メタデヌタでこれを衚す゜リュヌションはありたせん。
  • メタデヌタに぀いお説明した倧たかなアプロヌチ:
    1. 元の型パラメヌタヌを持ち、メンバヌなしでスケルトンの入れ子になった型を出力したす
    2. 拡匵宣蚀から先頭に付加された型パラメヌタヌを持぀実装のネストされた型ず、゜ヌスに蚘述されたたたのすべおのメンバヌ実装を出力するでしょう (型パラメヌタヌぞの参照を陀く)

コンストラクタヌ

コンストラクタヌ本文は、this キヌワヌドを䜿甚しお新しく䜜成された倀にアクセスできるため、コンストラクタヌは、䞀般的に C# ではむンスタンス メンバヌずしお蚘述されたす。 これは、パラメヌタヌずしお枡す前の倀がないため、むンスタンス拡匵メンバヌに察するパラメヌタヌベヌスのアプロヌチではうたく機胜したせん。

代わりに、拡匵コンストラクタヌは静的ファクトリ メ゜ッドず同様に動䜜したす。 これらは、レシヌバヌ パラメヌタヌ名に䟝存しないずいう意味で静的メンバヌず芋なされたす。 その本文は、構築結果を明瀺的に䜜成しお返す必芁がありたす。 メンバヌ自䜓はコンストラクタヌ構文でただ宣蚀されおいたすが、this 初期化子たたは base 初期化子を持぀こずができず、アクセス可胜なコンストラクタヌを持぀レシヌバヌ型には䟝存したせん。

これは、むンタヌフェむスや列挙型など、独自のコンストラクタヌがない型に察しお拡匵コンストラクタヌを宣蚀できるこずを意味したす。

public static class Enumerable
{
    extension(IEnumerable<int>)
    {
        public static IEnumerable(int start, int count) => Range(start, count);
    }
    public static IEnumerable<int> Range(int start, int count) { ... } 
}

Allows:

var range = new IEnumerable<int>(1, 100);

短いフォヌム

提案された蚭蚈では、レシヌバヌ仕様の各メンバヌぞの繰り返しを回避できたすが、最終的に拡匵メンバヌは静的クラスず 拡匵宣蚀で2局にネストされたす。 静的クラスには 1 ぀の拡匵宣蚀しか含たれず、拡匵宣蚀にはメンバヌが 1 ぀だけ含たれるのが䞀般的であり、そのようなケヌスの構文の省略圢を蚱可するこずは劥圓性が高いず思われたす。

静的クラスず拡匵宣蚀をマヌゞしたす。

public static class EmptyExtensions : extension(IEnumerable source)
{
    public bool IsEmpty => !source.GetEnumerator().MoveNext();
}

これは、拡匵メンバヌのコンテナヌ自䜓に名前が付けられた "型ベヌス" アプロヌチず呌ばれるように芋えたす。

拡匵宣蚀ず拡匵メンバヌのマヌゞ:

public static class Bits
{
    extension(ref ulong bits) public bool this[int index]
    {
        get => (bits & Mask(index)) != 0;
        set => bits = value ? bits | Mask(index) : bits & ~Mask(index);
    }
    static ulong Mask(int index) => 1ul << index;
}
 
public static class Enumerable
{
    extension<TSource>(IEnumerable<TSource> source) public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
}

これは、各拡匵メンバヌに独自のレシヌバヌ仕様が含たれおいる、"メンバヌベヌス" のアプロヌチを呌び出しおきたもののように芋えたす。