3

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

明らかに、ここで根本的なことを誤解しています。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していました。のインターフェイスがコンパイル時に設定されていたとしても、実行時に の入力が行われると想定しました。宣言は実際に行っているため、ここでは誤解を招くようですTTAnotherTypeTTT foo

AnotherType foo = new T();

footype として実際に宣言していないことを理解すると、メソッドの非表示が機能しないT理由が理解できます。new

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

4

2 に答える 2

3

宣言されたメソッドはnew、(コンパイラの観点から) 基本クラスの同じ名前/シグネチャを持つメソッドとは関係がありません。これは単に、基本クラス階層のメソッドとシグネチャを共有する派生クラスでさまざまなメソッドを定義できるようにするコンパイラの方法です。

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

new/virtual/override については多くの混乱があります。このSOの質問を見てください-ジェイソンとエリックの答えは素晴らしいです。同様の質問に対する Jon Skeet の回答も、実装がそのように動作する理由を理解するのに役立つ場合があります。

この問題を回避するには、次の 2 つの方法があります。

  1. ジェネリック クラスの派生型 (またはインターフェイス) への (実行時の型情報に基づく)条件付きキャストを実行します。これにより、カプセル化が中断され、望ましくない結合が追加されます。また、実装が不十分な場合も脆弱です。
  2. 気になるメソッドを公開するジェネリック制約で使用するインターフェイスを定義します。派生元のコードが変更できない場合、これは不可能な場合があります。
于 2010-04-15T22:53:15.040 に答える
1

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

于 2010-04-15T23:02:17.680 に答える