web-dev-qa-db-ja.com

3つの基本クラスを継承しようとしてもできない

私のUserクラスの設計に関連するいくつかの質問がありますが、それらは十分に異なっているので、独立した質問であるべきだと思います。

そのため、最初のものは基本クラスの継承に関連しています。現在、ProfileBaseISessionMgrEntryの2つのクラスを継承しています。

public class User : ProfileBase, ISessionMgrEntry

しかし、次のように3番目のクラスMembershipUserも継承したいと思います。

public class User : ProfileBase, MembershipUser, ISessionMgrEntry

ただし、コンパイラーはそれを許可しません。どうして?そして、どうすればこれを回避できますか?

ありがとう。

PS-ASP.NET 3.5/C#

[〜#〜] edit [〜#〜]

こんにちは。私が達成しようとしているものには、以下の解決策が役立つと思います。それは非常にシンプルで簡単なようです。これを行っているので、完全な/結合されたUserオブジェクトを作成できます。誰かがこれが問題を引き起こすかもしれない理由を知っていますか?私が丁をつけている間に出てきたのは、プロパティが重複していることです。たとえば、MembershipUserProfileBaseの両方が「UserName」を共有します。どちらか一方を選択する必要がありますか、またはこれは設計上の欠陥になりますか?提案?再度、感謝します。

 public class User
    {

        #region Constructors
            private readonly MembershipUser _MembershipUser;
            private readonly ProfileBase _ProfileBase;
        #endregion

        public User()
        {
            _MembershipUser = new MembershipUser();
            _ProfileBase = new ProfileBase();

        }

        public string Comment
        {
            get { return _MembershipUser.Comment as string; }
            set { _MembershipUser.Comment = value; }
        }

        public bool IsAnonymous
        {
            get { return _ProfileBase.IsAnonymous as bool; }
        } 

        ....

   }
27
Peter

最初の例では、実際には2つのクラスからではなく、1つのクラスとインターフェースから継承しています。

C#では、クラスからの複数の継承は許可されていませんが、複数のインターフェイスを実装できます。見る このMSDNブログの投稿 (リンクは無効ですので、以下にテキストを貼り付けます)理由の詳細については。

IMembershipUserインターフェイスを作成し、Userクラスに実装する必要があります。

インターフェイスには、通常、Iが前に付いた具体的なクラス名に基づいた名前が付けられます。したがって、クラスMembershipUserにはインターフェースIMembershipUserがあります。他の名前を使用するのを妨げるものは何もありませんが、インターフェイスを使用するすべての人がこの命名規則に慣れています。

複数実装の継承を直接実装しない理由はいくつかあります。 (ご存知のように、複数インターフェイスの継承をサポートしています)。

ただし、コンパイラがCLR内で型のMIを作成することは可能です。このパスをたどると、いくつかのラフなエッジがあります。結果は検証不可能であり、CLSを介した他の言語との相互運用性はありません。V1およびV1.1では、OSローダーロックでデッドロックが発生する可能性があります。 (最後の問題を修正していますが、最初の2つの問題は残っています)。この手法は、RVAベースの静的フィールドでいくつかのVTableを生成することです。マネージメソッドのアドレス(おそらくまだJITされていない)をデポジットするには、VTFixupコンストラクトを使用します。この構成は、トリプレットの表です。トリプレットは、マネージメソッドへのトークン、修正する必要があるイメージ内のアドレス(この場合、RVAベースの静的で作成するVTableのスロット)、およびいくつかのフラグで構成されます。可能なフラグはcorhdr.hで説明されており、32ビットと64ビットのポインターサイズを指定し、仮想動作を制御し、最終的にディスパッチするサンクの形で逆PInvoke動作を適用するかどうかを指定できます。マネージメソッド。アンマネージドからマネージドへの移行を実行している場合は、呼び出しをディスパッチするためにどのAppDomainを選択する必要があるかも制御できます。ただし、これらのオプションの1つ(COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN)はV1には存在しません。 V1.1で追加しました。

複数の実装継承の焼き付け済みの検証可能なCLS準拠バージョンを提供していない理由はいくつかあります。

  1. 実際、MIはどのように機能するかについて、言語によって期待が異なります。たとえば、競合がどのように解決されるか、重複するベースがマージされるか冗長であるかなど。 MIをCLRに実装する前に、すべての言語の調査を行い、共通の概念を理解し、それらを言語に依存しない方法で表現する方法を決定する必要があります。また、MIがCLSに属しているかどうか、およびこの概念を必要としない言語(おそらくVB.NETなど)にとってこれが何を意味するかを決定する必要があります。もちろん、それは私たちが共通言語ランタイムとして行っているビジネスですが、まだMIでそれを行うには至っていません。

  2. MIが本当に適切な場所の数は実際には非常に少ないです。多くの場合、代わりに複数のインターフェイスの継承によりジョブを完了できます。また、カプセル化と委任を使用できる場合もあります。ミックスインのようなわずかに異なる構成を追加する場合、実際にはより強力になりますか?

  3. 複数の実装の継承は、実装に多くの複雑さを注入します。この複雑さは、キャスティング、レイアウト、ディスパッチ、フィールドアクセス、シリアル化、ID比較、検証可能性、リフレクション、ジェネリック、およびおそらく他の多くの場所に影響を与えます。

この機能がそれ自体に対価をもたらすかどうかはまったく明らかではありません。よく聞かれる質問です。デューデリジェンスを行っていないものです。しかし、私の腸は、私たちが深い検査を行った後、私たちはまだ機能を実装しないままにしておくことを決定することを教えてくれます。

38
ChrisF

c#では、1つのクラスから継承が可能ですが、必要なだけ多くのインターフェイスを実装できます。あなたの場合、ProfileBaseMembershipUserはクラスであり、ISessionMgrEntryはインターフェースです。

6
nWorx

C#は単一継承のみをサポートします。クラスを連結する(つまり、MembershipUserProfileBaseから継承する)か、interfacesを使用することができます。

4
Jamie Ide

C#は 多重継承 を許可しません。

C#は単一の継承のみを許可します。1つの基本クラスからのみ継承できます。最初の場合、ISessionMgrEntryは、基本クラスとは異なるインターフェースです。違いに関する良い記事を次に示します。 http://msdn.Microsoft.com/en-us/library/ms973861.aspx

2番目のケースでは、コンパイラが許可しないProfileBaseMembershipUserの両方から継承しようとしています。これを回避する最善の方法は、ProfileBaseMembershipUserのいずれか(または両方)をカプセル化することです。

2
Jeff Hornby

C#は多重継承をサポートしていません。

最初の例では、ProfileBaseを継承し、ISessionMgrEntryインターフェイスを実装するようにクラスを宣言しています。

必要な数のインターフェイスをクラスに追加できます(すべてを実装する場合)。

2
Oded

同じメンバー名を持つ複数のインターフェースは問題ではありません。メンバーが同じ実装を共有できる場合は、同じ名前のパブリックメンバーを作成するだけです。同じ実装を共有できない場合は、メンバーの1つ(またはすべて)を明示的に実装できます。

たとえば、_IEnumerable<T>_はIEnumerableを継承し、両方とも異なる戻り型を持つGetEnumerator()メソッドを提供します。したがって、IEnumerable.GetEnumerator()は一般に明示的に実装されます。

_class Foo : IEnumerable<object>{
    // explicitly implement IEnumerable.GetEnumerator()
    IEnumerator IEnumerable.GetEnumerator ()
    {
        return GetEnumerator();
    }

    public IEnumerator<object> GetEnumerator ()
    {
        yield break;
    }
}
_

GetEnumerator()IEnumerable.GetEnumerator()を呼び出すことは、無限再帰呼び出しではありません。同じ名前のパブリックメンバー、not自体(つまり、IEnumerable.GetEnumerator()メソッド)を呼び出します。

0
jonp