为什么继承嵌套接口会导致 C# 中的依赖循环?
Why does inheriting nested interfaces cause a dependency cycle in C#?
本例编译:
public interface IOuter : IInner {
}
public interface IInner {
}
这也是:
public class Outer : Outer.IInner {
public interface IInner { }
}
但是当Outer
是一个接口时,我们得到一个编译错误:
public interface IOuter : IOuter.IInner {
public interface IInner { }
}
IOuter.cs(3, 18): [CS0529] Inherited interface 'IOuter.IInner' causes a cycle in the interface hierarchy of 'IOuter'
为什么继承嵌套接口会造成循环?
IInner
是否以某种方式隐式继承 IOuter
,从而阻止 IOuter
实现 IInner
?看起来不像,因为这样编译:
public interface IOuter {
string OuterProperty { get; }
public interface IInner {
string InnerProperty { get; }
}
}
public class InnerImplementation : IOuter.IInner {
// We only need to implement IInner's members, not IOuter's,
// so IOuter does not seem to be part of the dependency hierarchy here
public string InnerProperty { get; }
}
我在 C# language spec 的接口部分看不到任何关于嵌套接口的信息。嵌套接口通常只是未定义的行为吗?如果是这样,有什么原因吗?
编辑
查看类型声明的语言规范,here,它说:
A type_declaration can occur as a top-level declaration in a compilation unit or as a member declaration within a namespace, class, or struct.
这里没有指定接口,所以这可能确实是未定义的行为。尽管如此,如果有人知道嵌套接口类型是否曾被考虑过并被拒绝过,我想知道为什么。
- 请参阅以下答案。我看错了规范的部分。
类 存在相同的行为:
// doesn't compile either (with a different error)
public class Outer : Outer.IInner {
public class Inner { }
}
这在规范中指定 here:
It is a compile-time error for a class to depend on itself. For the purpose of this rule, a class directly depends on its direct base class (if any) and directly depends on the nearest enclosing class within which it is nested (if any). Given this definition, the complete set of classes upon which a class depends is the transitive closure of the directly depends on relationship.
另请参阅:Why can't a class extend its own nested class in C#?
不能对接口执行此操作的限制是此规则的扩展,也将接口考虑在内。您在规范中看不到任何有关此内容的原因是因为该规范仅适用于 C# 6,并且仅在 C# 8 中添加了编写嵌套接口的能力。您必须查看 Default Interface Methods under the "C# 8 features" section to find the new changes to the "Interfaces" section to the spec. It is stated in the binding base clauses 部分:
Interfaces now contain types. These types may be used in the base
clause as base interfaces. When binding a base clause, we may need to
know the set of base interfaces to bind those types (e.g. to lookup in
them and to resolve protected access). The meaning of an interface's
base clause is thus circularly defined. To break the cycle, we add a
new language rules corresponding to a similar rule already in place
for classes.
While determining the meaning of the interface_base of an interface,
the base interfaces are temporarily assumed to be empty. Intuitively
this ensures that the meaning of a base clause cannot recursively
depend on itself.
We used to have the following rules:
<the rules quoted above>
[...]
We are adjusting them as follows:
When a class B
derives from a class A
, it is a compile-time error for
A
to depend on B
. A class directly depends on its direct base class
(if any) and directly depends on the type within which it is
immediately nested (if any).
When an interface IB
extends an interface IA
, it is a compile-time
error for IA
to depend on IB
. An interface directly depends on its
direct base interfaces (if any) and directly depends on the type
within which it is immediately nested (if any).
Given these definitions, the complete set of types upon which a type
depends is the reflexive and transitive closure of the directly
depends on relationship.
根据 specification:
It is a compile-time error for a class to depend on itself. For the purpose of this rule, a class directly depends on its direct base class (if any) and directly depends on the nearest enclosing class within which it is nested (if any)
其中一个例子是:
class A : B.C {}
class B : A
{
public class C {}
}
results in a compile-time error because A
depends on B.C
(its direct base class), which depends on B
(its immediately enclosing class), which circularly depends on A
.
即B.C
取决于 B
C# 8 之前的接口无法声明类型(fiddle) which was changed with introduction of default interface methods and it seems that the same rules apply here, check binding base clauses 规范部分:
When an interface IB
extends an interface IA
, it is a compile-time error for IA
to depend on IB
. An interface directly depends on its direct base interfaces (if any) and directly depends on the type within which it is immediately nested (if any).
本例编译:
public interface IOuter : IInner {
}
public interface IInner {
}
这也是:
public class Outer : Outer.IInner {
public interface IInner { }
}
但是当Outer
是一个接口时,我们得到一个编译错误:
public interface IOuter : IOuter.IInner {
public interface IInner { }
}
IOuter.cs(3, 18): [CS0529] Inherited interface 'IOuter.IInner' causes a cycle in the interface hierarchy of 'IOuter'
为什么继承嵌套接口会造成循环?
IInner
是否以某种方式隐式继承 IOuter
,从而阻止 IOuter
实现 IInner
?看起来不像,因为这样编译:
public interface IOuter {
string OuterProperty { get; }
public interface IInner {
string InnerProperty { get; }
}
}
public class InnerImplementation : IOuter.IInner {
// We only need to implement IInner's members, not IOuter's,
// so IOuter does not seem to be part of the dependency hierarchy here
public string InnerProperty { get; }
}
我在 C# language spec 的接口部分看不到任何关于嵌套接口的信息。嵌套接口通常只是未定义的行为吗?如果是这样,有什么原因吗?
编辑
查看类型声明的语言规范,here,它说:
A type_declaration can occur as a top-level declaration in a compilation unit or as a member declaration within a namespace, class, or struct.
这里没有指定接口,所以这可能确实是未定义的行为。尽管如此,如果有人知道嵌套接口类型是否曾被考虑过并被拒绝过,我想知道为什么。
- 请参阅以下答案。我看错了规范的部分。
类 存在相同的行为:
// doesn't compile either (with a different error)
public class Outer : Outer.IInner {
public class Inner { }
}
这在规范中指定 here:
It is a compile-time error for a class to depend on itself. For the purpose of this rule, a class directly depends on its direct base class (if any) and directly depends on the nearest enclosing class within which it is nested (if any). Given this definition, the complete set of classes upon which a class depends is the transitive closure of the directly depends on relationship.
另请参阅:Why can't a class extend its own nested class in C#?
不能对接口执行此操作的限制是此规则的扩展,也将接口考虑在内。您在规范中看不到任何有关此内容的原因是因为该规范仅适用于 C# 6,并且仅在 C# 8 中添加了编写嵌套接口的能力。您必须查看 Default Interface Methods under the "C# 8 features" section to find the new changes to the "Interfaces" section to the spec. It is stated in the binding base clauses 部分:
Interfaces now contain types. These types may be used in the base clause as base interfaces. When binding a base clause, we may need to know the set of base interfaces to bind those types (e.g. to lookup in them and to resolve protected access). The meaning of an interface's base clause is thus circularly defined. To break the cycle, we add a new language rules corresponding to a similar rule already in place for classes.
While determining the meaning of the interface_base of an interface, the base interfaces are temporarily assumed to be empty. Intuitively this ensures that the meaning of a base clause cannot recursively depend on itself.
We used to have the following rules:
<the rules quoted above>
[...]
We are adjusting them as follows:
When a class
B
derives from a classA
, it is a compile-time error forA
to depend onB
. A class directly depends on its direct base class (if any) and directly depends on the type within which it is immediately nested (if any).When an interface
IB
extends an interfaceIA
, it is a compile-time error forIA
to depend onIB
. An interface directly depends on its direct base interfaces (if any) and directly depends on the type within which it is immediately nested (if any).Given these definitions, the complete set of types upon which a type depends is the reflexive and transitive closure of the directly depends on relationship.
根据 specification:
It is a compile-time error for a class to depend on itself. For the purpose of this rule, a class directly depends on its direct base class (if any) and directly depends on the nearest enclosing class within which it is nested (if any)
其中一个例子是:
class A : B.C {}
class B : A
{
public class C {}
}
results in a compile-time error because
A
depends onB.C
(its direct base class), which depends onB
(its immediately enclosing class), which circularly depends onA
.
即B.C
取决于 B
C# 8 之前的接口无法声明类型(fiddle) which was changed with introduction of default interface methods and it seems that the same rules apply here, check binding base clauses 规范部分:
When an interface
IB
extends an interfaceIA
, it is a compile-time error forIA
to depend onIB
. An interface directly depends on its direct base interfaces (if any) and directly depends on the type within which it is immediately nested (if any).