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

目前,我必须在运行时使用IsSubclassOfinside比较 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

表单的约束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));
    }
}

不幸的是,IncludeMappingOf没有T1类型参数,因此无法推断类型参数。调用它时,您必须指定两种类型:

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

这通常可以通过更改 API 以包含参数(即truckMapping.IncludeMappingOf(vehicleMapping))、更改参数所在的方法/类或在流式 API 中创建链(即mapping.Of<Vehicle>().Include())来解决。

于 2015-06-25T12:25:20.723 回答