Her*_*ken 7 c# generics type-parameter
我有这种情况(大大简化):
interface IPoint<TPoint>
where TPoint:IPoint<TPoint>
{
//example method
TPoint Translate(TPoint offset);
}
interface IGrid<TPoint, TDualPoint>
where TPoint:IPoint<T
where TDualPoint:Ipoint
{
TDualPoint GetDualPoint(TPoint point, /* Parameter specifying direction */);
}
Run Code Online (Sandbox Code Playgroud)
这是典型的实现:
class HexPoint : IPoint<HexPoint> { ... }
class TriPoint : IPoint<TriPoint> { ... }
class HexGrid : IGrid<HexPoint, TriPoint> { ... }
class TriGrid : IGrid<TriPoint, HexPoint> { ... }
Run Code Online (Sandbox Code Playgroud)
所以在a上HexGrid,客户端可以调用双网格上的点,具有完全正确的类型:
TriPoint dual = hexGrid.GetDualPoint(hexPoint, North);
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好; 客户端不需要知道关于两点如何关联的类型的任何信息,她需要知道的是HexGrid该方法GetDualPoint返回一个TriPoint.
除了...
我有一个充满通用算法的类,它们在IGrids 上运行,例如:
static List<TPoint> CalcShortestPath<TPoint, TDualPoint>(
IGrid<TPoint, TDualPoint> grid,
TPoint start,
TPoint goal)
{...}
Run Code Online (Sandbox Code Playgroud)
现在,客户突然有知道小细节的双重点HexPoint是TriPoint,我们需要将其指定为类型参数列表的一部分,尽管它并非严格没关系此算法:
static List<TPoint> CalcShortestPath<TPoint, *>(
IGrid<TPoint, *> grid,
TPoint start,
TPoint goal)
{...}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我想让DualPoint成为该类型的"属性" IPoint,因此这HexPoint.DualPoint 就是类型TriPoint.
允许IGrid看起来像这样的东西:
interface IGrid<TPoint>
where TPoint:IPoint<TPoint>
//and TPoint has "property" DualPoint where DualPoint implements IPoint...
{
IGrid<TPoint.DualPoint> GetDualGrid();
}
Run Code Online (Sandbox Code Playgroud)
和函数CalcShortestPath这样
static List<TPoint> CalcShortestPath<TPoint>(
IGrid<TPoint> grid,
TPoint start,
TPoint goal)
{...}
Run Code Online (Sandbox Code Playgroud)
当然,据我所知,这是不可能的.
但有没有办法可以改变我的设计以模仿这种方式?以便
为了说明为什么这成为一个真正的问题:在我的库中IGrid实际上有4个类型参数,IPoint有3个,两者都可能增加(最多6个和5个).(大多数类型参数之间保持相似的关系.)
显式重载而不是算法的泛型是不实际的:每个IGrid和的有9个具体实现IPoint.一些算法在两种类型的网格上运行,因此具有一吨类型参数.(许多函数的声明比函数体长!)
当我的IDE 在自动重命名期间丢弃所有类型参数时,心理负担被驱逐回家,我不得不手动将所有参数放回去.这不是一个无意识的任务; 我的大脑被炒了.
根据@Iridium的请求,显示何时类型推断失败的示例.显然,下面的代码没有做任何事情; 它只是为了说明编译器的行为.
using System;
using System.Collections.Generic;
using System.Linq;
public interface IPoint<TPoint, TDualPoint>
where TPoint:IPoint<TPoint, TDualPoint>
where TDualPoint : IPoint<TDualPoint, TPoint>{}
interface IGrid<TPoint, TDualPoint>
where TPoint:IPoint<TPoint, TDualPoint>
where TDualPoint:IPoint<TDualPoint, TPoint>{}
class HexPoint : IPoint<HexPoint, TriPoint>
{
public HexPoint Rotate240(){ return new HexPoint();} //Normally you would rotate the point
}
class TriPoint : IPoint<TriPoint, HexPoint>{}
class HexGrid : IGrid<HexPoint, TriPoint>{}
static class Algorithms
{
public static IEnumerable<TPoint> TransformShape<TPoint, TDualPoint>(
IEnumerable<TPoint> shape,
Func<TPoint, TPoint> transform)
where TPoint : IPoint<TPoint, TDualPoint>
where TDualPoint : IPoint<TDualPoint, TPoint>
{
return
from TPoint point in shape
select transform(point);
}
public static IEnumerable<TPoint> TransformShape<TPoint, TDualPoint>(
IGrid<TPoint, TDualPoint> grid,
IEnumerable<TPoint> shape,
Func<TPoint, TPoint> transform)
where TPoint : IPoint<TPoint, TDualPoint>
where TDualPoint : IPoint<TDualPoint, TPoint>
{
return
from TPoint point in shape
//where transform(point) is in grid
select transform(point);
}
}
class UserCode
{
public static void UserMethod()
{
HexGrid hexGrid = new HexGrid();
List<HexPoint> hexPointShape = new List<HexPoint>(); //Add some items
//Compiles
var rotatedShape1 = Algorithms.TransformShape(
hexGrid,
hexPointShape,
point => point.Rotate240()).ToList();
//Compiles
var rotatedShape2 = Algorithms.TransformShape<HexPoint, TriPoint>(
hexPointShape,
point => point.Rotate240()).ToList();
//Does not compile
var rotatedShape3 = Algorithms.TransformShape(
hexPointShape,
point => point.Rotate240()).ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
所以,根据我在评论中谈到的一次性想法提出一个答案......
基本要点是“定义一个传达点对偶性概念的类型,并在相关签名中使用它,以便为编译器提供所需的提示”
每当遇到可怕的“无法从使用情况推断类型”错误时,您应该阅读一件事: http: //blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-签名的.aspx
在那方面,先生。Lippert 阐明了残酷的事实,即在此推理阶段仅检查签名的参数,而不检查约束。所以我们在这里必须更“具体”一点。
首先,让我们定义我们的“二元关系”——我应该指出,这是建立这些关系的一种方式,(理论上)它们有无限多种。
public interface IDual<TPoint, TDualPoint>
where TPoint: IPoint<TPoint>, IDual<TPoint, TDualPoint>
where TDualPoint: IPoint<TDualPoint>, IDual<TDualPoint, TPoint>
{}
Run Code Online (Sandbox Code Playgroud)
现在我们回去改造我们现有的签名:
public interface IPoint<TPoint>
where TPoint:IPoint<TPoint>
{}
class TriPoint : IPoint<TriPoint>, IDual<TriPoint,HexPoint>
{}
class HexPoint : IPoint<HexPoint>, IDual<HexPoint,TriPoint>
{
// Normally you would rotate the point
public HexPoint Rotate240(){ return new HexPoint();}
}
Run Code Online (Sandbox Code Playgroud)
同样,在“次要类型”上,网格:
interface IGrid<TPoint, TDualPoint>
where TPoint: IPoint<TPoint>, IDual<TPoint, TDualPoint>
where TDualPoint : IPoint<TDualPoint>, IDual<TDualPoint, TPoint>
{
TDualPoint GetDualPoint(TPoint point);
}
class HexGrid : IGrid<HexPoint, TriPoint>
{
public TriPoint GetDualPoint(HexPoint point)
{
return new TriPoint();
}
}
class TriGrid : IGrid<TriPoint, HexPoint>
{
public HexPoint GetDualPoint(TriPoint point)
{
return new HexPoint();
}
}
Run Code Online (Sandbox Code Playgroud)
最后是我们的实用方法:
static class Algorithms
{
public static IEnumerable<TPoint> TransformShape<TPoint, TDualPoint>(
IEnumerable<IDual<TPoint, TDualPoint>> shape,
Func<TPoint, TPoint> transform)
where TPoint : IPoint<TPoint>, IDual<TPoint, TDualPoint>
where TDualPoint : IPoint<TDualPoint>, IDual<TDualPoint, TPoint>
{
return
from TPoint point in shape
select transform(point);
}
public static IEnumerable<TPoint> TransformShape<TPoint, TDualPoint>(
IGrid<TPoint, TDualPoint> grid,
IEnumerable<IDual<TPoint, TDualPoint>> shape,
Func<TPoint, TPoint> transform)
where TPoint : IPoint<TPoint>, IDual<TPoint, TDualPoint>
where TDualPoint : IPoint<TDualPoint>, IDual<TDualPoint, TPoint>
{
return
from TPoint point in shape
//where transform(point) is in grid
select transform(point);
}
}
Run Code Online (Sandbox Code Playgroud)
注意方法上的签名 - 我们说“嘿,我们给你的这个列表,它绝对有双重点”,这将允许像这样的代码:
HexGrid hexGrid = new HexGrid();
List<HexPoint> hexPointShape = new List<HexPoint>(); //Add some items
//Compiles
var rotatedShape1 = Algorithms
.TransformShape(
hexGrid,
hexPointShape,
point => point.Rotate240())
.ToList();
//Compiles
var rotatedShape2 = Algorithms
.TransformShape<HexPoint, TriPoint>(
hexPointShape,
point => point.Rotate240())
.ToList();
//Did not compile, but does now!
var rotatedShape3 = Algorithms
.TransformShape(
hexPointShape,
point => point.Rotate240())
.ToList();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
219 次 |
| 最近记录: |