簡體   English   中英

將類型參數約束為基本類型

[英]Constrain type parameter to a base type

我知道如何強制類型參數成為另一種類型的類型:

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中的IncludeMappingOf來比較T1和T2。 編譯安全的解決方案更可取。 有任何想法嗎?

編輯:改變示例,以減少設計臭。

注意:鏈接的問題非常相似,但沒有給出合適的答案。 希望這個問題也會對這個問題有所了解。

編輯#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的情況!

雖然w0lf的答案提供了直接的解決方案,但我想給出一些背景解釋。

當你寫的東西像

class C<A> where A : B

要么

void F<A>() where A : B

形式A : B的約束必須將A作為聲明的類,接口,方法等的泛型類型參數之一。

您面臨的錯誤不是因為您在冒號的右側放置了當前聲明的泛型類型參數(這是合法的) - 這是因為您已經放置了外部聲明的泛型類型參數(不是當前聲明)在結腸的左側。

如果要在某個聲明上形成約束A : B ,則必須在該聲明中引入A ,並且A的范圍必須小於或等於B的范圍。 究其原因,這是一個務實的語言的限制是,對於任何泛型類型參數T ,它隔離了關於類型約束任何推理T到單個聲明T被引入。

在類(接口)級別聲明泛型類型和泛型約束:

public interface IMapping<T1, T2> where T2 : T1
{
    void IncludeMapping(IMapping<T1, T2> otherMapping);
}

您可以使用擴展方法來接近您想要的。 使用您的持有者示例將是:

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));
    }
}

遺憾的是, IncludeMappingOf沒有T1類型的參數,因此無法推斷出類型參數。 調用它時,您必須指定這兩種類型:

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

通常可以通過更改API以包含參數(即truckMapping.IncludeMappingOf(vehicleMapping) ),更改參數所在的方法/類或創建鏈的流暢API(即mapping.Of<Vehicle>().Include() )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM