【发布时间】:2018-11-05 09:41:22
【问题描述】:
我需要在 C# 中建模与跆拳道比赛相关的上下文,其中有一个由更多比赛组成的锦标赛。具体而言,就运动员的初始数量而言,一场比赛最多可以进行 3 场比赛:淘汰赛、半决赛和决赛。在每场比赛中,我都想跟踪运动员制作的 Poomsaes(一种运动员展览)。在淘汰赛或半决赛中,有一种特定的品势具有一定的预期结果,与决赛中的品势不同(不同的参数等)。
所以我有一个表示通用匹配的接口(其中包含能够处理特定类型的 PoomsaeResult 的 Poomsaes)。由于泛型类型的属性比较困难,我将这个概念分为两种方法。
public interface IMatch<R, P> where R : IPoomsaeResult where P : IPoomsae<R>
{
void AddScheduledPoomsae(P poomsae);
ISet<P> GetScheduledPoomsaes();
}
还有两个实现,它们的接口就是:
public interface IStandardMatch : IMatch<IStandardPoomsaeResult, IStandardPoomsae>
{
}
public interface IFinalMatch : IMatch<IFinalPoomsaeResult, IFinalPoomsae>
{
}
在锦标赛类中,我希望有这样一个字典,将锦标赛的每个阶段(淘汰赛、半决赛和决赛的枚举)与相关比赛相关联,以实现以下目标:
Dictionary<TournamentStage, IMatch> matches = new Dictionary<TournamentStage, IMatch>();
matches.Add(TournamentStage.Semifinal, new StandardMatch());
matches.Add(TournamentStage.Final, new FinalMatch());
问题是 IMatch 是通用的,即使我想支持任何类型的匹配,我也必须指定一个类型。有 C# 经验的人可以提出问题的解决方案或更好的建模方法吗?
IMatch 有两个泛型,因为我使用这种方法对 IPoomsae 进行了建模:
public interface IPoomsae<R> where R : IPoomsaeResult
{
int MemberId { get; }
R Result { get; }
}
public interface IStandardPoomsae : IPoomsae<IStandardPoomsaeResult>
{
// Extra
}
public interface IFinalPoomsae : IPoomsae<IFinalPoomsaeResult>
{
// Extra
}
事实上,每个品势都有一个与完成它的运动员相关的id和一种结果(标准和最终情况不同)。
【问题讨论】:
-
这似乎是一个坏掉的设计。当您以非通用方式存储它的实例时,通用接口有什么目的?话虽如此,即使您可以这样做,您希望在使用
matches[Tournament.SemiFinal]时获得什么类型的实例?它肯定只能返回一些 un 通用匹配,使您在Match上的所有通用方法都毫无用处。您不能指望编译器猜测要返回的对象的确切类型。 -
即使字典允许这样做,您也无法使用最终的元素,因为它们的类型和签名会有所不同。我建议不要过度设计。从一个静态函数 main 开始,只编写足够的代码让您的测试通过。特别是,我不需要 TournamentStage 课程。一个很好的类比是经常展示的保龄球游戏 kata。它可以预先设计好所有的类,或者尽可能简单。参见例如youtube.com/watch?v=xFGFk0Fnztg
-
这不是正确的方向。 match 的 kind 是一个属性,而不是一个类型。
-
TimCorey 有一个关于锦标赛跟踪器的 C# 教程。我并不是说您需要教程,而是因为锦标赛跟踪器是经典的第一个应用程序。一些教程将让您一目了然地了解其他人如何解决这个问题。如果您需要代码,github 上有大量副本)
-
谢谢大家。我想尝试采用一个通用的解决方案并避免像每个锦标赛阶段的不同属性所代表的刚性结构,但这导致我的设计错误(正如@HimBromBeere 所证明的那样)。我将使用 TDD 方法转向将匹配类型表示为属性的解决方案。
标签: c# generics collections