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
戻り値の型を持つには、型のオーバーライドメソッドが必要です public
。However 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 conversionbetween 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 methodM
declared in a classC
, the overridden base method is determined by examining each base class ofC
, starting with the direct base class ofC
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 asM
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 methodM
declared in an interfaceI
, the overridden base method is determined by examining each direct or indirect base interface ofI
, collecting the set of interfaces declaring an accessible method which has the same signature asM
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 memberA
matches an interface memberB
when:
A
とB
はメソッドであり、との名前、型、および仮パラメーターA
リストB
は同じです。A
andB
are methods, and the name, type, and formal parameter lists ofA
andB
are identical.A
とB
はプロパティであり、との名前と型は同じであり、とA
B
A
同じアクセサーを持ちB
A
ます (明示的なインターフェイスメンバーの実装でない場合は、追加のアクセサーを持つことができます)。A
andB
are properties, the name and type ofA
andB
are identical, andA
has the same accessors asB
(A
is permitted to have additional accessors if it is not an explicit interface member implementation).A
とB
はイベントであり、との名前と型はA
B
同じです。A
andB
are events, and the name and type ofA
andB
are identical.A
とB
はインデクサーで、との型および仮パラメーターA
リストB
は同じであり、とA
同じアクセサーをB
持ちA
ます (明示的なインターフェイスメンバーの実装でない場合は、追加のアクセサーを持つことができます)。A
andB
are indexers, the type and formal parameter lists ofA
andB
are identical, andA
has the same accessors asB
(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 memberA
matches an interface memberB
when:
A
とB
はメソッドであり、およびの名前と仮パラメーターのリストはA
B
同じです。の戻り値の型は、の戻り値のA
B
型に pid へ変換暗黙の参照の id を使用して、の戻り値の型に変換B
できます。A
andB
are methods, and the name and formal parameter lists ofA
andB
are identical, and the return type ofA
is convertible to the return type ofB
via an identity of implicit reference convertion to the return type ofB
.A
とB
はプロパティであり、との名前はA
B
同一であり、とA
同じアクセサーを持ちますB
(A
明示的なインターフェイスメンバーの実装でない場合は追加のアクセサーを持つことが許可されA
ます)B
。また、が readonly プロパティの場合は、A
暗黙的な参照変換です。A
andB
are properties, the name ofA
andB
are identical,A
has the same accessors asB
(A
is permitted to have additional accessors if it is not an explicit interface member implementation), and the type ofA
is convertible to the return type ofB
via an identity conversion or, ifA
is a readonly property, an implicit reference conversion.A
とB
はイベントであり、との名前と型はA
B
同じです。A
andB
are events, and the name and type ofA
andB
are identical.A
とB
はインデクサーで、との仮パラメーターリストはA
B
同一であり、とA
同じアクセサーを持ちますB
(A
明示的なインターフェイスメンバーの実装でない場合は、追加のアクセサーを持つことが許可されA
ます)B
。また、A
が読み取り専用インデクサーの場合は、暗黙的な参照変換によっての型がに変換可能です。A
andB
are indexers, the formal parameter lists ofA
andB
are identical,A
has the same accessors asB
(A
is permitted to have additional accessors if it is not an explicit interface member implementation), and the type ofA
is convertible to the return type ofB
via an identity conversion or, ifA
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
- については、「」 https://github.com/dotnet/roslyn/issues/357 をご覧ください。some discussion at https://github.com/dotnet/roslyn/issues/357.
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-01-08.md
- C# 9.0 でのみクラスメソッドのオーバーライドをサポートする決定に向けたオフラインディスカッション。Offline discussion toward a decision to support overriding of class methods only in C# 9.0.