我知道如何强制将类型参数作为另一种类型的子类型:
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);
编译器:参数类型“T1”不能赋予参数类型“T2”。 是的我知道,我正在尝试让编译器只允许T2可分配给参数类型T1的情况!
答案 0 :(得分:5)
虽然w0lf的答案提供了直接的解决方案,但我想提供一些背景解释。
当你写
之类的东西时class C<A> where A : B
或
void F<A>() where A : B
A : B
形式的约束必须具有A
作为声明的类,接口,方法等的泛型类型参数之一。
你所面临的错误是不是,因为你已经在冒号的右侧放置了当前声明的泛型类型参数(这是合法的) - 这是因为你放置了一个通用的冒号左侧的外部声明(不是当前声明)的类型参数。
如果要在某个声明上形成约束A : B
,则必须在该声明中引入A
,并且A
的范围必须小于或等于B
的范围{1}}。这是一个实用的语言限制的原因是,对于任何泛型类型参数T
,它将关于类型T
的约束的任何推理隔离到引入T
的单个声明。
答案 1 :(得分:4)
在类(接口)级别声明泛型类型和泛型约束:
public interface IMapping<T1, T2> where T2 : T1
{
void IncludeMapping(IMapping<T1, T2> otherMapping);
}
答案 2 :(得分:1)
您可以使用扩展方法接近您想要的内容。使用你的持有者示例,它将是:
var principalMock = new Mock<IPrincipal>(); // Mock<T> is from the Moq framework
principalMock.Setup(x => x.IsAuthenticated).Returns(true); // Or false, depending on what you're testing
var httpContextMock = new Mock<HttpContextBase>();
httpContextMock.Setup(x => x.User).Returns(principalMock.Object);
var controllerContext = new ControllerContext { HttpContext = contextMock.Object };
conrollerContext.Controller = controller;
controller.ControllerContext = controllerContext;
然后允许您的示例调用代码进行编译而不会出现错误:
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
通常可以通过更改API以包含参数(即var mapping = MapManager.Find<Truck>();
mapping.IncludeMappingOf<Truck, Vehicle>();
mapping.Serialize(new TonkaTruck());
),更改参数所在的方法/类或创建链的流畅API(即truckMapping.IncludeMappingOf(vehicleMapping)
)来解决这个问题。