8

型パラメーターを強制的に別の型のサブタイプにする方法を知っています。

public interface IMapping<T2> 
{
    public void Serialize<T3>(T3 obj) 
        where T3 : T2;
}
...

var mapping = MapManager.Find<Truck>();
mapping.Serialize(new TonkaTruck());

型パラメータを強制的に別の型のスーパータイプにする方法はありますか?

public interface IMapping<T2>
{
    public void IncludeMappingOf<T1>() 
        where T2 : T1;   // <== doesn't work
}
...

var mapping = MapManager.Find<Truck>();

// Truck inherits Vehicle    
// Would like compiler safety here:
mapping.IncludeMappingOf<Vehicle>(); 

mapping.Serialize(new TonkaTruck());

IsSubclassOf現在、実行時に insideを使用して T1 と T2 を比較する必要がありますIncludeMappingOf。コンパイルセーフなソリューションが望ましいでしょう。何か案は?

編集: デザインの臭いが少なくなるように例を変更しました。

注:リンクされた質問は非常に似ていますが、適切な回答はありません。うまくいけば、この質問はその質問にも光を当てます。

編集#2:

より簡単な例:

public class Holder<T2>
{
    public T2 Data { get; set; }

    public void AddDataTo<T1>(ICollection<T1> coll)
        //where T2 : T1    // <== doesn't work
    {
        coll.Add(Data);   // error
    }
}

...
var holder = new Holder<Truck> { Data = new TonkaTruck() };
var list = new List<Vehicle>();
holder.AddDataTo(list);

コンパイラ: 引数の型 'T2' は、パラメーターの型 'T1' に割り当てられません。 はい、わかっています。T2 がパラメーター タイプ T1 に割り当て可能な場合のみを許可するようにコンパイラーを取得しようとしています。

4

3 に答える 3

5

w0lfの答えは直接的な解決策を提供しますが、背景について説明したいと思います。

みたいなことを書くと

class C<A> where A : B

また

void F<A>() where A : B

フォームの制約は、宣言されているクラス、インターフェイス、メソッドなどのジェネリック型パラメーターの 1 つとしてA : B持つ必要があります。A

あなたが直面しているエラーは、現在の宣言のジェネリック型パラメーターをコロンの右側に配置したためではありません(これは合法です)。外側の宣言のジェネリック型パラメーターを配置したためです (現在の宣言) コロンの左側にあります。

A : Bある宣言に制約を形成したい場合はA、その宣言に を導入する必要があり、 のスコープは のスコープA以下でなければなりませんB。これが実用的な言語の制限である理由は、ジェネリック型パラメーターTの場合、型の制約に関するすべての推論が、導入されてTいる単一の宣言に分離されるためです。T

于 2013-09-03T15:56:11.643 に答える
4

クラス (インターフェイス) レベルでジェネリック型とジェネリック制約の両方を宣言します。

public interface IMapping<T1, T2> where T2 : T1
{
    void IncludeMapping(IMapping<T1, T2> otherMapping);
}
于 2013-09-03T15:51:47.827 に答える
1

拡張メソッドを使用して、目的に近づけることができます。ホルダーの例を使用すると、次のようになります。

public class Holder<T2>
{
    public T2 Data { get; set; }
}

public static class HolderExtensions
{
    public static void AddDataTo<T2, T1>(this Holder<T2> holder, ICollection<T1> coll)
        where T2 : T1
    {
        coll.Add(holder.Data);
    }
}

これにより、サンプル呼び出しコードをエラーなしでコンパイルできます。

var holder = new Holder<Truck> { Data = new TonkaTruck() };
var list = new List<Vehicle>();
holder.AddDataTo(list);

マッピングの例は、インターフェイスであるため複雑です。既存のインターフェイスから拡張メソッドを実装する方法がない場合は、実装メソッドをインターフェイスに追加する必要がある場合があります。つまり、実行時チェックは引き続き必要ですが、呼び出し元は適切な構文とコンパイル時間チェックを取得できます。それは次のようになります。

public interface IMapping<T2>
{
    void IncludeMappingOf(Type type);
}

public static class MappingExtensions
{
    public static void IncludeMappingOf<T2, T1>(this IMapping<T2> mapping)
        where T2 : T1
    {
        mapping.IncludeMappingOf(typeof(T1));
    }
}

残念ながら、 にIncludeMappingOfT1型のパラメーターがないため、型パラメーターを推測できません。呼び出すときは、両方のタイプを指定する必要があります。

var mapping = MapManager.Find<Truck>();
mapping.IncludeMappingOf<Truck, Vehicle>();
mapping.Serialize(new TonkaTruck());

これは多くの場合、API を変更してパラメーターを含める(truckMapping.IncludeMappingOf(vehicleMapping)つまりmapping.Of<Vehicle>().Include()

于 2015-06-25T12:25:20.723 に答える