11

提供されたジェネリック型パラメーターに基づいて具体的な実装イ​​ンスタンスを提供する単純なファクトリ メソッドがあります。具象クラスが型パラメーターを持つ共通の抽象基本クラスから継承する場合、それらをキャストできません。コンパイラが教えてくれError 2 Cannot convert type 'Car' to 'VehicleBase<T>'ます。同じ型パラメーターを持つインターフェイスを抽象クラスに置き換えるか、抽象クラスからジェネリック型パラメーターを削除すると、正常に機能します。

interface IWheel
{
}

class CarWheel : IWheel
{
}

abstract class VehicleBase<T>
{
}

class Car : VehicleBase<CarWheel>
{
}

class VehicleFactory
{
    public static VehicleBase<T> GetNew<T>()
    {
        if (typeof(T) == typeof(CarWheel))
        {
            return (VehicleBase<T>)new Car();
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

これは でのコンパイルに失敗します(VehicleBase<T>)new Car()。これはコンパイラの欠陥ですか?それとも、抽象クラスと型パラメータを持つインターフェイスを異なる方法で扱うための意図的な設計上の決定でしょうか?

回避策として、抽象クラスにインターフェイスを実装させ、これをファクトリ メソッドの戻り値として使用することはできますが、それでもこの動作が発生する理由を知りたいです。

4

3 に答える 3

9

ジェネリックコードはすべての可能なものに対して(同じILで)機能する必要があるため、これは証明できません。たとえば、それは言うまでもありません。コンパイラーは、チェックが発生するという事実を過度に分析しません。つまり、静的チェッカーは各ステートメントを個別に処理し、条件の因果関係を理解し​​ようとはしません。TCar : VehicleBase<float>ifTCarWheel

強制するにobjectは、真ん中にキャストします。

return (VehicleBase<T>)(object)new Car();

でも!あなたのアプローチは、それ自体は実際には「一般的」ではありません。

于 2012-06-26T06:30:42.133 に答える
6

これはコンパイラの欠陥でも、意図的な決定でもありません。ジェネリッククラスの型パラメーターは共変でも反変でもありません。つまり、同じジェネリッククラスの特殊化の間に継承関係はありません。ドキュメントから:

.NET Frameworkバージョン4では、バリアント型パラメーターはジェネリックインターフェイスとジェネリックデリゲート型に制限されています。

これは、抽象クラスの代わりにインターフェイスを使用するため、次のコードがコンパイルされることを意味します。

interface IWheel
{
}

class CarWheel : IWheel
{
}

interface IVehicleBase<T> 
{
}

class Car : IVehicleBase<CarWheel>
{
}

class VehicleFactory
{
    public static IVehicleBase<T> GetNew<T>() 
    {
        if (typeof(T) == typeof(CarWheel))
        {
            return (IVehicleBase<T>)new Car();
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

詳細と例については、「ジェネリック医薬品の共変性と反変性」を確認してください。

C#FAQブログには、共変性と反変性に関するFAQもあり、詳細と11部構成のシリーズがあります。エリック・リッパートによる主題について

于 2012-06-26T06:44:57.110 に答える
3

これはうまくいくようです:

return new Car() as VehicleBase<T>;

私の推測では、そのようになっています。
のジェネリック型インスタンスはVehicleBase<T>関連していないため、それらをキャストすることが機能することを証明することはできません。

ここに画像の説明を入力してください

Tタイプがの場合Blah、キャストは機能しません。オブジェクトに戻ってから他のブランチを取得することはできません(結局、C#には多重継承はありません)。

以前にキャストし直すことobjectで、キャストが機能する可能性が再び開かれます。これは、までのパスがまだ存在する可能性があるためVehicleBase<CarWheel>です。もちろん、インターフェースはこのツリーの下のどこobjectにでも表示できるので、それも機能するはずです。

于 2012-06-26T06:30:49.547 に答える