9

これは、C# で 1 回か 2 回私を捕まえました。このようなコードを書くことができます

class Node
{
    class Connection
    {
        public Connection(Node node, string label)
        {
            this.Node = node;
            this.Label = label;
        }
        public Node Node { get; private set;  }
        public string Label { get; private set; }            
    };

    IEnumerable<Connection> IncomingConnections() // ...
    IEnumerable<Connection> OutgoingConnections() // ...
}

しかし、私が書くと

interface INode
{
    class Connection
    {
        public Connection(INode node, string label)
        {
            this.Node = node;
            this.Label = label;
        }
        public INode Node { get; private set; }
        public string Label { get; private set; }
    };

    IEnumerable<Connection> IncomingConnections();
    IEnumerable<Connection> OutgoingConnections();
}

コンパイルエラーが発生します

エラー CS0524: '接続': インターフェイスは型を宣言できません

制限は理解していますが、私が興味を持っているのは理由です。私は確かにC++の「インターフェース」(これは抽象メンバーを持つ単なるクラスであるため、当然のことです)でネストされた型を持つことができます.Javaでも可能であるようです.Interfaces Cannot Declare Type Issue C#を参照してください. C# が Java からいくつかのことを学んだことを考えると、なぜこの点が欠けているのでしょうか (実際に欠けているとすれば)。

(これがすでに他の場所で対処されている場合はお詫び申し上げます。Interfaces cannot declare typesWhy can't I put a delegate in an interface?も見つかりましたが、私の質問に直接対処していないようです。)

編集

Java の世界では、インターフェイス内にクラスをネストしてもよいかどうかについて、一見未解決の問題のように思われるというメモを追加したいと思います。https://stackoverflow.com/a/9098321/834521を参照してください。同じことが C# に適用できない理由を尋ねるのはばかげているとは思いません。

編集

Framework Design Guidelines、第 2 版、セクション 4.9 pp115-117からの簡単な要約/引用。

  • たとえば、ネストされた型が外側の型のプライベート メンバーにアクセスする必要がある場合は、ネストされた型を使用してください。
  • グループ化に public のネストされた型を使用しないでください。これには名前空間を使用します。
  • 自分が何をしているのか本当にわかっていない限り、パブリックにネストされた型は避けてください。(主な動機: ネストされた型を明示的に作成することは、スキルの低い開発者にとって混乱を招きます。ただし、コレクション列挙子などを介して暗黙的に作成することは問題ありません。)
  • ネストされた型が含まれる型の外部で使用またはインスタンス化される場合は、ネストされた型を使用しないでください(これらは両方とも、ネストされた型が含まれる型から独立していることを主張しています)。
  • インターフェイスのメンバーとして使用しないでください。
4

5 に答える 5

31

インターフェイスに型を含めることができないのはなぜですか?

質問を掘り下げる前に、いくつかのことを明確にさせてください。

まず、CLR 型システムでは、インターフェイス内に入れ子になった型が許可されます。C# や VB など、インターフェイス、デリゲート、クラス、構造体、および列挙型をインターフェイス内で宣言することをサポートするバージョンを作成することは完全に可能であり、既存の CLR で実行されます。

次に、「なぜ C# 言語は機能 X を実装していないのですか?」という形式の質問に対して、いつものように反論します。答えは、X のすべての値について同じです。機能を実装するには、考え、設計し、仕様を定め、実装し、テストし、顧客に出荷する必要があります。これらの 6 つのいずれかが発生しない場合、機能はありません。これらのうちの 1 つ以上が発生しなかったため、機能 X は実装されていません。

第 3 に、C# コンパイラ チーム (私はもう所属していません) は、機能を実装しないことについて説明する必要はありません。機能にはお金がかかり、予算には限りがあります。したがって、機能を要求した人には、その費用に対してその利点を正当化する責任があります。

第 4 に、「なぜ」の質問は答えるのが難しく、「なぜしない」の質問はさらに難しい。

というわけで、あなたの質問を却下し、私が答えられる質問に置き換えます。

この機能要求が C# 設計チームに提案されたとします。あなたはそれに対してどのような議論をしたでしょうか?

  • この機能は、CLR では合法ですが、CLS では合法ではありません。CLS では合法ではない C# の機能がたくさんありますが、CLS のガイダンスでは、ほとんどの言語でサポートされていないため、インターフェイスに型をネストしないでください であるため、C# で機能を実装することは、本質的に、できないライブラリを作成することを人々に奨励しています。他の言語で使用できます。提案された機能は、悪いプログラミング プラクティスを助長します

  • ネストされた型には、主に 3 つの利点があります。まず、それらを囲む型のプライベート メンバーにアクセスできます。これは、プライベート メンバーを持たないインターフェイスの利点ではありません。次に、外部型の特定のプライベート実装の詳細を含める便利な方法を提供します。これは、ネストされたプライベートな型を持つことができず、定義により実装の詳細を持たないインターフェイスにとってはメリットがありません。第 3 に、あるタイプを別のタイプに関連付ける便利な方法を提供します。ただし、これは名前空間によって行う方が適切です。

  • 私の知る限り、この機能を要求している人は他にいません。顧客が望んでいる機能がたくさんあるのに、ほとんど誰も望んでいない機能にお金を使うのはやめましょう。

  • この機能を実装しても、それ自体で言語がより強力になったり、より表現力豊かになったりするわけではありません。

  • この機能を実装することは、私が認識しているさらに優れた機能への足がかりではありません。この機能は、他の「テーマ」とは関係ありません。これは、有用な機能ではなく、小さな非直交性を排除する「完全主義」機能です。

  • この機能がない場合の簡単な回避策があります。ネストされた型を最上位の型にするだけです。

それは反対の場合です。誰かがこの機能の主張を進めなければ、設計委員会の会議でおそらく 5 分以上続くことはありません。この機能のケースを進めてもよろしいですか?

于 2013-04-22T19:17:17.450 に答える
3

追加したいのは、C# 8.0 から、インターフェイスはネストされた型を使用できるようになったことです。

デフォルトのインターフェイス メソッド - C# 8.0 仕様の提案 | Microsoft Docs (強調を追加)

インターフェイスの構文は、許可するように拡張されています。

  • 定数、演算子、静的コンストラクター、およびネストされた型を宣言するメンバー宣言。

したがって、以下のようなものは現在合法です。

interface ISpectrum {
    [Flags]
    enum Palette { Red = 1, Green = 2, Blue = 4 }
    Palette Color { get; }
}

これが良い習慣であるかどうかは他の回答で議論されていますが、私は個人的にインターフェース固有の列挙型がその用途を持っていることを発見しました。

興味深いことに、この変更は既定のインターフェイス実装の一部としてリストされていますが、そのほとんどは新しいランタイム、つまり .NET Core 3.0/.NET Standard 2.1 以降を必要としますが、入れ子になった型を持つインターフェイスは実装されずにコンパイルされ、で使用できます。 C# 8.0 でのコンパイルをサポートする Roslyn CSC が使用されている限り、.NET Framework 4.8。

Eric Lippert が回答 here で述べているように、これは、CLR がインターフェイスでネストされた型をこれまでずっとサポートしてきたという事実によるものだと思います。

于 2020-05-01T02:52:59.177 に答える
2

型を入れ子にすることが理にかなっている理由はいくつかあります。主な理由は、コンテナー クラスのみがアクセスできるように、それらをプライベートとして定義することです。コンテナー クラスは、独自の実装でこれらのプライベート型を使用します。

インターフェイスは実装ではないため、その中に型をネストする正当な理由はありません。それは役に立たないでしょう。それは、農夫が子猫を使って畑を耕すのを手伝おうとするようなものです。理論的には、少なくとも試すことは可能かもしれませんが、実際には何の役にも立たないでしょう。

Connection提供されたコードを見て、クラスを最上位の型に昇格することをお勧めします。関数に従って型を整理したい場合、それが名前空間の目的です。型がどのように編成されているかを示すフォルダー構造をプロジェクトに作成し、それを反映するように名前空間を変更します。

于 2013-04-22T16:17:37.230 に答える
1

C# 仕様のセクション 13.2 から:

インターフェイスには、定数、フィールド、演算子、インスタンス コンストラクター、デストラクター、または型を含めることはできません。また、インターフェイスに任意の種類の静的メンバーを含めることもできません。

ネストされた型は一種の静的メンバーであるため、ネストされた型を特殊なケースにするよりも、許可しない方が一貫性があります。

于 2013-04-22T16:19:01.873 に答える