一般的な制約のあるクラスを使用して、メソッド隠蔽(新しい)の使用方法

StackOverflow https://stackoverflow.com/questions/2649514

  •  27-09-2019
  •  | 
  •  

質問

いくつかの基本クラスに制約されている汎用パラメーターを備えたコンテナクラスがあります。ジェネリックに提供されるタイプは、基本クラスの制約のサブです。サブクラスは、メソッドハイディング(新しい)を使用して、ベースクラスからメソッドの動作を変更します(いいえ、コードではないため、仮想にすることはできません)。私の問題は、「新しい」メソッドが呼び出されないことです。コンパイラは、提供されたタイプを、まるでベースにアップキャストしたかのように、サブではなくベースクラスであると考えているようです。

明らかに私はここで何か基本的な何かを誤解しています。ジェネリックだと思いました where T: xxx アップキャストタイプではなく制約でした。

このサンプルコードは基本的に私が話していることを示しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericPartialTest
{
    class ContextBase
    {
        public string GetValue()
        {
            return "I am Context Base: " + this.GetType().Name;
        }

        public string GetOtherValue()
        {
            return "I am Context Base: " + this.GetType().Name;
        }

    }

    partial class ContextSub : ContextBase
    {
        public new string GetValue()
        {
            return "I am Context Sub: " + this.GetType().Name;
        }
    }

    partial class ContextSub
    {
        public new string GetOtherValue()
        {
            return "I am Context Sub: " + this.GetType().Name;
        }
    }

    class Container<T> where T: ContextBase, new()
    {
        private T _context = new T();

        public string GetValue()
        {
            return this._context.GetValue();
        }

        public string GetOtherValue()
        {
            return this._context.GetOtherValue();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Simple");
            ContextBase myBase = new ContextBase();
            ContextSub mySub = new ContextSub();

            Console.WriteLine(myBase.GetValue());
            Console.WriteLine(myBase.GetOtherValue());
            Console.WriteLine(mySub.GetValue());
            Console.WriteLine(mySub.GetOtherValue());

            Console.WriteLine("Generic Container");
            Container<ContextBase> myContainerBase = new Container<ContextBase>();
            Container<ContextSub> myContainerSub = new Container<ContextSub>();

            Console.WriteLine(myContainerBase.GetValue());
            Console.WriteLine(myContainerBase.GetOtherValue());
            Console.WriteLine(myContainerSub.GetValue());
            Console.WriteLine(myContainerSub.GetOtherValue());


            Console.ReadKey();
        }
    }
}

編集:

私の混乱はそれからこれを行うことができると思います

class SomeClass<T> where T: AnotherType, new()
{
    T foo = new T();     
}

そして、私は期待していました T することが T コンパイラが表示することを理解していても T 持っているように AnotherTypeのインターフェイス。のタイピングを想定しました T のインターフェイスがあっても、実行時に発生します T コンパイル時間に設定されました。 T foo 宣言は、本当にやっているので、ここで誤解を招くようです

AnotherType foo = new T();

それが実際に宣言されていないことを理解したら foo タイプとして T, 、なぜそれは理解できます new メソッドの隠すことは機能しません。

そして、それが私がそれについて言わなければならないすべてです。

役に立ちましたか?

解決

宣言された方法 new ベースクラスに同じ名前/署名を持つメソッドとの関係(コンパイラの観点から)はありません。 これは、基本クラスの典型的なメソッドと署名を共有する派生クラスでさまざまなメソッドを定義できるコンパイラの方法です。

これで、特定のケースに関して、ジェネリックが汎用パラメーターとして提供されるタイプに関係なく、genericsが単一のバイトコードにコンパイルする必要があることを認識してください. 。その結果、コンパイラは、一般的なタイプtで定義されているメソッドとプロパティのみを知っています - それは一般的な制約で指定するベースタイプです。コンパイラはについて何も知りません new 派生タイプのインスタンスをパラメーターとして導き出した型のインスタンスを作成したとしても、派生タイプのメソッド。したがって、汎用クラスでの呼び出しは、常にベースタイプのメソッドに移動します。

新/仮想/オーバーライドについては多くの混乱があります。 を取る この質問を見てください - ジェイソンとエリックの答えは素晴らしいです。 ジョン・スキートの答え 同様の質問にとって、あなたの実装がそれがそうであるように振る舞う理由を理解するのにも役立つかもしれません。

この問題を回避するための2つの可能な方法があります。

  1. 条件付きキャストを実行します (ランタイムタイプ情報に基づく)一般的なクラスの派生タイプ(またはインターフェイス)へ。これにより、カプセル化が破壊され、望ましくない結合が追加されます。不十分に実装されていれば壊れやすいです。
  2. インターフェイスを定義します あなたが気にする方法を公開する一般的な制約で使用すること。これは、あなたが派生しているコードが変更できるものではない場合、不可能です。

他のヒント

おもう これはとても質問です 私はあなたを助けるかもしれないと尋ねました。それが不可能な理由については、ジョン・スキートの答えを参照してください。

別のレイヤーを追加 - サードパーティのクラスではなく、サードパーティから継承する新しいクラスからジェネリックを継承します。この新しいクラスでは、問題のメソッドを新しい仮想として定義できます。すべてのコードが第3部のクラスを直接参照しない場合、それは動作するはずです

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top