私はこの状況にあります(劇的に単純化されています):
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 */);
}
典型的な実装は次のとおりです。
class HexPoint : IPoint<HexPoint> { ... }
class TriPoint : IPoint<TriPoint> { ... }
class HexGrid : IGrid<HexPoint, TriPoint> { ... }
class TriGrid : IGrid<TriPoint, HexPoint> { ... }
したがって、HexGrid
で、クライアントは、正確に正しいタイプで、デュアルグリッド上のポイントを取得するために呼び出しを行うことができます。
TriPoint dual = hexGrid.GetDualPoint(hexPoint, North);
ここまでは順調ですね; クライアントは、2つのポイントがどのように関連するかについてタイプについて何も知る必要はありません。クライアントが知る必要があるのはHexGrid
、メソッドでメソッドGetDualPoint
がを返すことだけTriPoint
です。
それ外...
IGrid
たとえば、sで動作する一般的なアルゴリズムでいっぱいのクラスがあります。
static List<TPoint> CalcShortestPath<TPoint, TDualPoint>(
IGrid<TPoint, TDualPoint> grid,
TPoint start,
TPoint goal)
{...}
HexPoint
ここで、クライアントは突然、aのデュアルポイントがであるという詳細を知るTriPoint
必要があります。このアルゴリズムでは厳密には重要ではありませんが、タイプパラメータリストの一部として指定する必要があります。
static List<TPoint> CalcShortestPath<TPoint, *>(
IGrid<TPoint, *> grid,
TPoint start,
TPoint goal)
{...}
理想的には、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();
}
そしてCalcShortestPath
このような機能
static List<TPoint> CalcShortestPath<TPoint>(
IGrid<TPoint> grid,
TPoint start,
TPoint goal)
{...}
もちろん、私が知る限り、これは不可能です。
しかし、どういうわけかこれを模倣するようにデザインを変更する方法はありますか?となることによって
- 2つのタイプの関係を表現しています
- 過剰な型引数リストを防ぎます
- これにより、クライアントは、具象型が実装するインターフェースの型パラメーターを具体的な型がどのように「特殊化」するかについて、あまり深く考える必要がなくなります。
これが実際の問題になる理由を示すために:私のライブラリには、IGrid
実際には4つのタイプパラメータがIPoint
あり、3つあり、両方が潜在的に増加します(最大6と5)。(これらのタイプパラメータのほとんどの間にも同様の関係があります。)
アルゴリズムのジェネリックスの代わりに明示的なオーバーロードは実用的ではありません。とのそれぞれに9つの具体的な実装がIGrid
ありIPoint
ます。一部のアルゴリズムは2種類のグリッドで動作するため、1トンの種類のパラメーターがあります。(多くの関数の宣言は関数本体よりも長くなります!)
自動名前変更中に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();
}
}