为什么继承嵌套接口会导致 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).