covariant の戻り値Covariant returns

まとめSummary

共変の戻り値の型 をサポートします。Support covariant return types. 具体的には、メソッドをオーバーライドして、オーバーライドするメソッドよりも派生した戻り値の型を宣言し、読み取り専用プロパティのオーバーライドでより多くの派生型を宣言できるようにします。Specifically, permit the override of a method to declare a more derived return type than the method it overrides, and similarly to permit the override of a read-only property to declare a more derived type. より多くの派生型で使用されるオーバーライド宣言は、基本型のオーバーライドに示されているように、少なくとも特定の戻り値の型を指定する必要があります。Override declarations appearing in more derived types would be required to provide a return type at least as specific as that appearing in overrides in its base types. メソッドまたはプロパティの呼び出し元は、より洗練された戻り値の型を呼び出しから静的に受け取ります。Callers of the method or property would statically receive the more refined return type from an invocation.

目的Motivation

これは、オーバーライドされたメソッドと同じ型を返す必要がある言語の制約を回避するために、さまざまなメソッド名を開発する必要がある、コード内の一般的なパターンです。It is a common pattern in code that different method names have to be invented to work around the language constraint that overrides must return the same type as the overridden method.

これは、ファクトリパターンで役に立ちます。This would be useful in the factory pattern. たとえば、Roslyn コードベースでは、次のようになります。For example, in the Roslyn code base we would have

class Compilation ...
{
    public virtual Compilation WithOptions(Options options)...
}
class CSharpCompilation : Compilation
{
    public override CSharpCompilation WithOptions(Options options)...
}

詳細なデザインDetailed design

これは、C# での 共変の戻り値の型 の仕様です。This is a specification for covariant return types in C#. この目的は、メソッドのオーバーライドによって、オーバーライドされるメソッドよりも派生した戻り値の型を返すこと、および読み取り専用のプロパティをオーバーライドしてより派生した戻り値の型を返すことを許可することです。Our intent is to permit the override of a method to return a more derived return type than the method it overrides, and similarly to permit the override of a read-only property to return a more derived return type. メソッドまたはプロパティの呼び出し元は、より洗練された戻り値の型を呼び出しから静的に受信します。また、より多くの派生型で表示されるオーバーライドは、その基本型のオーバーライドに示されているように、少なくとも特定の戻り値の型を指定する必要があります。Callers of the method or property would statically receive the more refined return type from an invocation, and overrides appearing in more derived types would be required to provide a return type at least as specific as that appearing in overrides in its base types.


クラスメソッドのオーバーライドClass Method Override

クラスオーバーライドメソッドの既存の制約The existing constraint on class override methods

  • オーバーライドメソッドとオーバーライドされた基本メソッドの戻り値の型は同じです。The override method and the overridden base method have the same return type.

がに変更されましたis modified to

  • オーバーライドメソッドには、id 変換によって変換可能な戻り値の型、またはオーバーライドされた基本メソッドの戻り値の型への暗黙の参照変換が含まれている必要があります。The override method must have a return type that is convertible by an identity conversion or (if the method has a value return - not a ref return) implicit reference conversion to the return type of the overridden base method.

さらに、次の追加要件がリストに追加されます。And the following additional requirements are appended to that list:

  • オーバーライドメソッドには、オーバーライドメソッドの (直接または間接) 基本型で宣言されたオーバーライドされた基本メソッドのすべてのオーバーライドの戻り値の型への暗黙の参照変換 (id 変換によって変換可能な戻り値の型が含まれている必要があります)。The override method must have a return type that is convertible by an identity conversion or (if the method has a value return - not a ref return) implicit reference conversion to the return type of every override of the overridden base method that is declared in a (direct or indirect) base type of the override method.
  • オーバーライドメソッドの戻り値の型は、少なくともオーバーライドメソッド (アクセシビリティドメイン) と同じようにアクセス可能である必要があります。The override method's return type must be at least as accessible as the override method (Accessibility domains).

この制約により、クラスのオーバーライドメソッドは private 戻り値の型を持つことが private できます。This constraint permits an override method in a private class to have a private return type. ただし、 public public 戻り値の型を持つには、型のオーバーライドメソッドが必要です publicHowever it requires a public override method in a public type to have a public return type.

クラスのプロパティとインデクサーのオーバーライドClass Property and Indexer Override

クラスオーバーライドプロパティの既存の制約The existing constraint on class override properties

オーバーライドするプロパティの宣言では、継承されたプロパティとまったく同じアクセシビリティ修飾子と名前を指定する必要があります。また、 オーバーライドするの型と継承されたプロパティの間でid 変換を行う必要があります。An overriding property declaration shall specify the exact same accessibility modifiers and name as the inherited property, and there shall be an identity conversion between the type of the overriding and the inherited property. 継承されたプロパティにアクセサーが1つしかない場合 (つまり、継承されたプロパティが読み取り専用または書き込み専用の場合)、オーバーライドする側のプロパティにはそのアクセサーだけが含まれます。If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property shall include only that accessor. 継承されたプロパティに両方のアクセサーが含まれている場合 (つまり、継承されたプロパティが読み取り/書き込みの場合)、オーバーライドする側のプロパティには、1つのアクセサーまたは両方のアクセサーを含めることができます。If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors.

がに変更されましたis modified to

オーバーライドするプロパティの宣言では、継承されたプロパティとまったく同じアクセシビリティ修飾子と名前を指定する必要があります 。また、(継承されたプロパティが読み取り専用で、戻り値が refではない場合) 暗黙の参照変換は、オーバーライドするプロパティの型から継承されたプロパティの型に変換 します。An overriding property declaration shall specify the exact same accessibility modifiers and name as the inherited property, and there shall be an identity conversion or (if the inherited property is read-only and has a value return - not a ref return) implicit reference conversion from the type of the overriding property to the type of the inherited property. 継承されたプロパティにアクセサーが1つしかない場合 (つまり、継承されたプロパティが読み取り専用または書き込み専用の場合)、オーバーライドする側のプロパティにはそのアクセサーだけが含まれます。If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property shall include only that accessor. 継承されたプロパティに両方のアクセサーが含まれている場合 (つまり、継承されたプロパティが読み取り/書き込みの場合)、オーバーライドする側のプロパティには、1つのアクセサーまたは両方のアクセサーを含めることができます。If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors. オーバーライドするプロパティの型は、オーバーライドする側のプロパティ (アクセシビリティドメイン) と少なくともアクセス可能である必要があります。The overriding property's type must be at least as accessible as the overriding property (Accessibility domains).


以下のドラフト仕様の残りの部分では、後で考慮するインターフェイスメソッドの共変の戻り値をさらに拡張しています。The remainder of the draft specification below proposes a further extension to covariant returns of interface methods to be considered later.

インターフェイスメソッド、プロパティ、およびインデクサーのオーバーライドInterface Method, Property, and Indexer Override

C# 8.0 の DIM 機能の追加により、インターフェイスで許可されるメンバーの種類にを追加すると共に、メンバーのサポートと override 共変の戻り値が追加されます。Adding to the kinds of members that are permitted in an interface with the addition of the DIM feature in C# 8.0, we further add support for override members along with covariant returns. これらは override クラスに指定されたメンバーの規則に従いますが、次の点が異なります。These follow the rules of override members as specified for classes, with the following differences:

クラスの次のテキスト。The following text in classes:

オーバーライド宣言によってオーバーライドされるメソッドは、オーバーライドされた 基本メソッド と呼ばれます。The method overridden by an override declaration is known as the overridden base method. クラスで宣言されたオーバーライドメソッドの場合、オーバーライドされる基本メソッドは、の M C 各基底クラスを調べて、後続の各直接基底クラスを続行することによって決定され C C ます。これは、指定された基底クラスの型の中で、 M 型引数の置換後と同じシグネチャを持つアクセス可能なメソッドが少なくとも1つ存在For an override method M declared in a class C, the overridden base method is determined by examining each base class of C, starting with the direct base class of C and continuing with each successive direct base class, until in a given base class type at least one accessible method is located which has the same signature as M after substitution of type arguments.

には、インターフェイスに対応する仕様が指定されています。is given the corresponding specification for interfaces:

オーバーライド宣言によってオーバーライドされるメソッドは、* オーバーライドされた 基本メソッド _ と呼ばれます。The method overridden by an override declaration is known as the *overridden base method _. インターフェイスで宣言されたオーバーライドメソッドの場合、 M I オーバーライドされる基本メソッドは、の直接または間接の基本インターフェイスを調べて、 I M 型引数の置換後と同じシグネチャを持つ、アクセス可能なメソッドを宣言するインターフェイスのセットを収集することによって決定されます。For an override method M declared in an interface I, the overridden base method is determined by examining each direct or indirect base interface of I, collecting the set of interfaces declaring an accessible method which has the same signature as M after substitution of type arguments. このインターフェイスのセットに、このセット内のすべての型からの id または暗黙の参照変換が存在する _most 派生型 * がある場合、その型には、オーバーライドされた 基本メソッド が含まれます。If this set of interfaces has a _most derived type*, to which there is an identity or implicit reference conversion from every type in this set, and that type contains a unique such method declaration, then that is the overridden base method.

同様に、 override 15.7.6 Virtual、sealed、override、および abstract の各アクセサーのクラスに指定されているインターフェイスで、プロパティとインデクサーを許可します。We similarly permit override properties and indexers in interfaces as specified for classes in 15.7.6 Virtual, sealed, override, and abstract accessors.

名前参照Name Lookup

クラス宣言が存在する場合の名前参照では、現在、検出された override メンバーの詳細が、 override 識別子の修飾子の型 (または this 修飾子がない場合) から始まるクラス階層内の最も派生した宣言から検出されます。Name lookup in the presence of class override declarations currently modify the result of name lookup by imposing on the found member details from the most derived override declaration in the class hierarchy starting from the type of the identifier's qualifier (or this when there is no qualifier). たとえば、12.6.2.2 に 対応するパラメーター には、For example, in 12.6.2.2 Corresponding parameters we have

クラスで定義された仮想メソッドとインデクサーの場合、パラメーターリストは、受信側の静的な型から開始するときに見つかった関数メンバーの最初の宣言またはオーバーライドから選択され、その基底クラスを検索します。For virtual methods and indexers defined in classes, the parameter list is picked from the first declaration or override of the function member found when starting with the static type of the receiver, and searching through its base classes.

これには、次のように追加します。to this we add

インターフェイスで定義された仮想メソッドとインデクサーの場合、パラメーターリストは、関数メンバーのオーバーライドの宣言を含む型の中で、最も派生する型の中で見つかった関数メンバーの宣言またはオーバーライドから選択されます。For virtual methods and indexers defined in interfaces, the parameter list is picked from the declaration or override of the function member found in the most derived type among those types containing the declaration of override of the function member. このような一意の型が存在しない場合、コンパイル時エラーになります。It is a compile-time error if no unique such type exists.

プロパティまたはインデクサーアクセスの結果型については、既存のテキストFor the result type of a property or indexer access, the existing text

  • インスタンスプロパティを識別した場合、結果は、関連付けられたインスタンス式 E と、プロパティの型である関連する型を持つプロパティアクセスになります。If I identifies an instance property, then the result is a property access with an associated instance expression of E and an associated type that is the type of the property. T がクラス型の場合、関連付けられている型は、最初の宣言または、T で始まるときに見つかったプロパティのオーバーライドから選択され、その基本クラスを検索します。If T is a class type, the associated type is picked from the first declaration or override of the property found when starting with T, and searching through its base classes.

はによって強化されます。is augmented with

T がインターフェイス型の場合、関連付けられている型は、T の最派生クラスまたはその直接または間接的な基本インターフェイスで見つかったプロパティの宣言またはオーバーライドから選択されます。If T is an interface type, the associated type is picked from the declaration or override of the property found in the most derived of T or its direct or indirect base interfaces. このような一意の型が存在しない場合、コンパイル時エラーになります。It is a compile-time error if no unique such type exists.

12.7.7.3 インデクサーアクセス で同様の変更を行う必要があります。A similar change should be made in 12.7.7.3 Indexer access

12.7.6 呼び出し式 では、既存のテキストを拡張します。In 12.7.6 Invocation expressions we augment the existing text

  • それ以外の場合、結果は、メソッドまたはデリゲートの戻り値の型に関連付けられた型を持つ値になります。Otherwise, the result is a value, with an associated type of the return type of the method or delegate. 呼び出しがインスタンスメソッドで、受信側がクラス型 T の場合、関連付けられている型は、最初の宣言またはメソッドのオーバーライドから取得されます。このメソッドは、T で開始し、基本クラスを検索します。If the invocation is of an instance method, and the receiver is of a class type T, the associated type is picked from the first declaration or override of the method found when starting with T and searching through its base classes.

withwith

呼び出しがインスタンスメソッドであり、受信側がインターフェイス型 T の場合、関連付けられている型は、T とその直接および間接的な基本インターフェイスの中から最も派生インターフェイスにあるメソッドの宣言またはオーバーライドから選択されます。If the invocation is of an instance method, and the receiver is of an interface type T, the associated type is picked from the declaration or override of the method found in the most derived interface from among T and its direct and indirect base interfaces. このような一意の型が存在しない場合、コンパイル時エラーになります。It is a compile-time error if no unique such type exists.

暗黙的なインターフェイスの実装Implicit Interface Implementations

仕様のこのセクションThis section of the specification

インターフェイスマッピングのために、クラスメンバーは A 次の場合にインターフェイスメンバーと一致し B ます。For purposes of interface mapping, a class member A matches an interface member B when:

  • AB はメソッドであり、との名前、型、および仮パラメーター A リスト B は同じです。A and B are methods, and the name, type, and formal parameter lists of A and B are identical.
  • AB はプロパティであり、との名前と型は同じであり、と A B A 同じアクセサーを持ち B A ます (明示的なインターフェイスメンバーの実装でない場合は、追加のアクセサーを持つことができます)。A and B are properties, the name and type of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).
  • AB はイベントであり、との名前と型は A B 同じです。A and B are events, and the name and type of A and B are identical.
  • AB はインデクサーで、との型および仮パラメーター A リスト B は同じであり、と A 同じアクセサーを B 持ち A ます (明示的なインターフェイスメンバーの実装でない場合は、追加のアクセサーを持つことができます)。A and B are indexers, the type and formal parameter lists of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).

は次のように変更されます。is modified as follows:

インターフェイスマッピングのために、クラスメンバーは A 次の場合にインターフェイスメンバーと一致し B ます。For purposes of interface mapping, a class member A matches an interface member B when:

  • AB はメソッドであり、およびの名前と仮パラメーターのリストは A B 同じです。の戻り値の型は、の戻り値の A B 型に pid へ変換暗黙の参照の id を使用して、の戻り値の型に変換 B できます。A and B are methods, and the name and formal parameter lists of A and B are identical, and the return type of A is convertible to the return type of B via an identity of implicit reference convertion to the return type of B.
  • AB はプロパティであり、との名前は A B 同一であり、と A 同じアクセサーを持ちます B ( A 明示的なインターフェイスメンバーの実装でない場合は追加のアクセサーを持つことが許可され A ます) B 。また、が readonly プロパティの場合は、 A 暗黙的な参照変換です。A and B are properties, the name of A and B are identical, A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation), and the type of A is convertible to the return type of B via an identity conversion or, if A is a readonly property, an implicit reference conversion.
  • AB はイベントであり、との名前と型は A B 同じです。A and B are events, and the name and type of A and B are identical.
  • AB はインデクサーで、との仮パラメーターリストは A B 同一であり、と A 同じアクセサーを持ちます B ( A 明示的なインターフェイスメンバーの実装でない場合は、追加のアクセサーを持つことが許可され A ます) B 。また、 A が読み取り専用インデクサーの場合は、暗黙的な参照変換によっての型がに変換可能です。A and B are indexers, the formal parameter lists of A and B are identical, A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation), and the type of A is convertible to the return type of B via an identity conversion or, if A is a readonly indexer, an implicit reference conversion.

これは、次のプログラムが "C1" を出力するので、技術的には互換性に影響する変更です。"現在、" C2 "と出力します。"M" は、提案されたリビジョンの下にあります。This is technically a breaking change, as the program below prints "C1.M" today, but would print "C2.M" under the proposed revision.

using System;

interface I1 { object M(); }
class C1 : I1 { public object M() { return "C1.M"; } }
class C2 : C1, I1 { public new string M() { return "C2.M"; } }
class Program
{
    static void Main()
    {
        I1 i = new C2();
        Console.WriteLine(i.M());
    }
}

この重大な変更のため、暗黙的な実装では共変の戻り値の型をサポートしていないと考えられる場合があります。Due to this breaking change, we might consider not supporting covariant return types on implicit implementations.

インターフェイス実装に関する制約Constraints on Interface Implementation

明示的なインターフェイスの実装では、基底インターフェイスのオーバーライドで宣言された戻り値の型よりも、戻り値の型を宣言する必要があるという規則が必要です。We will need a rule that an explicit interface implementation must declare a return type no less derived than the return type declared in any override in its base interfaces.

API の互換性に関する影響API Compatibility Implications

TBDTBD

オープン中の問題Open Issues

この仕様では、呼び出し元がより洗練された戻り値の型を取得する方法については説明しません。The specification does not say how the caller gets the more refined return type. おそらく、呼び出し元が最も派生したオーバーライドのパラメーター仕様を取得するのと同様の方法で実行されます。Presumably that would be done in a way similar to the way that callers get the most derived override's parameter specifications.


次のようなインターフェイスがあるとします。If we have the following interfaces:

interface I1 { I1 M(); }
interface I2 { I2 M(); }
interface I3: I1, I2 { override I3 M(); }

では、 I3 メソッドとメソッド I1.M()I2.M() "マージ" されていることに注意してください。Note that in I3, the methods I1.M() and I2.M() have been “merged”. を実装するとき I3 は、両方を一緒に実装する必要があります。When implementing I3, it is necessary to implement them both together.

通常、元のメソッドを参照するには、明示的な実装が必要です。Generally, we require an explicit implementation to refer to the original method. この質問は、クラスに含まれています。The question is, in a class

class C : I1, I2, I3
{
    C IN.M();
}

これは何を意味するのでしょうか。What does that mean here? N はどのようなものですか?What should N be?

I1.Mまたは I2.M (両方ではなく) の実装を許可し、その両方の実装として扱うことをお勧めします。I suggest that we permit implementing either I1.M or I2.M (but not both), and treat that as an implementation of both.

デメリットDrawbacks

  • [] すべての言語の変更は、それ自体に対して支払う必要があります。[ ] Every language change must pay for itself.
  • [] 詳細継承階層の場合でも、パフォーマンスが妥当であることを確認する必要があります。[ ] We should ensure that the performance is reasonable, even in the case of deep inheritance hierarchies
  • [] 以前のコンパイラから新しい IL を使用している場合でも、翻訳戦略の成果物が言語のセマンティクスに影響しないようにする必要があります。[ ] We should ensure that artifacts of the translation strategy do not affect language semantics, even when consuming new IL from old compilers.

代替Alternatives

ソースでは、言語ルールを少し緩和して、We could relax the language rules slightly to allow, in source,

abstract class Cloneable
{
    public abstract Cloneable Clone();
}

class Digit : Cloneable
{
    public override Cloneable Clone()
    {
        return this.Clone();
    }

    public new Digit Clone() // Error: 'Digit' already defines a member called 'Clone' with the same parameter types
    {
        return this;
    }
}

未解決の質問Unresolved questions

  • [] この機能を使用するようにコンパイルされた Api は、以前のバージョンの言語で動作しますか。[ ] How will APIs that have been compiled to use this feature work in older versions of the language?

会議のデザインDesign meetings