类设计问题:列表联合<childstat>和AllStats </childstat>

时间:2011-04-06 21:39:46

标签: c# linq class

我有一个Player类和一个Stat类。 Player类有一个List属性,其中PlayerStat具有List和XP属性。我认为我的设计存在缺陷,因为我在做一些我觉得应该很容易的事情。我想列出所有玩家及其所有统计数据的XP。下面是类和GetCompletePlayerStats方法的更多细节,这是我真正不喜欢的。我基本上需要为玩家的所有统计数据列出XP,如果玩家没有统计数据,那么它的XP应为零。任何设计帮助/建议将不胜感激。

public class Stat : EntityBase{
  public virtual string Name { get; set; }
  public virtual UnitOfMeasure Unit { get; set; }
  public virtual int UnitXP { get; set; }
}
public class Player : EntityBase{
  public virtual string Name { get; set; }
  public virtual IList<PlayerStat> PlayerStats { get; set; }

  public virtual List<PlayerStat> GetCompletePlayerStats(IQueryable<Stat> stats)
    {
        var allStats = new List<PlayerStat>();
        var playerStatIds = PlayerStats.Select(ps => ps.PlayerStatistic.Id).ToList();
        if (playerStatIds.Count == 0)
        {
            allStats.AddRange(stats.Select(stat => new PlayerStat() {PlayerStatistic = stat, XP = 0}));
        }
        else
        {
            var zeroStats = stats.Where(s => !playerStatIds.Contains(s.Id)).ToList();

            allStats.AddRange(zeroStats.Select(zeroStat => new PlayerStat() {PlayerStatistic = zeroStat, XP = 0}));
            allStats.AddRange(PlayerStats);
        }

        return allStats;
    }

}
public class PlayerStat : EntityBase{
  public virtual Stat PlayerStatistic { get; set; }
  public virtual double XP { get; set; }
}

2 个答案:

答案 0 :(得分:0)

我不得不承认,到目前为止,我还没有真正看到你班级设计的一个主要缺点。当然,我对大局以及你的游戏设计没有任何见解,因为这只是它的一小部分。

但是,你说你不喜欢GetCompletePlayerStats。我必须多次阅读才能理解你在这里要做的事情。如果我看到了这一点,您只想返回与每个给定PlayerStat对象相对应的Stat对象。我猜Stat有一个Id字段(您在方法中使用它)来比较其中两个字段的语义相等。

鉴于我到目前为止做出了正确的假设(不幸的是,你没有提供太多信息),该方法可以简化为:

public virtual IEnumerable<PlayerStat> GetCompletePlayerStats(IEnumerable<Stat> stats)
{
    foreach(Stat stat in stats)
        yield return PlayerStats.FirstOrDefault(s => stat.Id == s.PlayerStatistic.Id) ?? 
                     new PlayerStat() {PlayerStatistic = stat, XP = 0};
    yield break;
}

这里的方法不需要IQueryable而是IEnumerable来通过foreach循环进行迭代,如果存在,则会产生相应的PlayerStats对象,或者创建一个XP设置为0的新的(否则空归合运算符??在这些情况下非常有用)。

希望有所帮助!

答案 1 :(得分:0)

使用现有设计,这种方法可以简化:

public virtual IList<PlayerStat> GetCompletePlayerStats(IEnumerable<Stat> stats)
{
   // build a dictionary of existing stats by ID to facilitate 
   // the join with requested stats (effectively doing a hash join)
   var playerStatsById = PlayerStats.ToDictionary(ps => ps.PlayerStatistic.Id);

   // for each requested stat, return either the corresponding player stat 
   // or a zero stat if one isn't found, maintaining the original order of stats
   return stats
       .Select(s => playerStatsById.ContainsKey(s.Id) ? 
           playerStatsById[s.Id] : 
           new PlayerStat { PlayerStatistic = s, XP = 0 })
       .ToList();
}

请注意,由于这实际上是一个外连接操作(您将statsPlayerStats连接,但您还需要将不匹配产生为零)您也可以使用LINQ来实现GroupJoin()操作,虽然我怀疑它的可读性会低得多。

关于你的设计让我烦恼的是名字含糊不清,例如你有PlayerStat类和PlayerStatistic属性,它们意味着微妙的不同;考虑以不同的方式命名,事情可能看起来更好;事实上,对于任何情况,这可能是我能给你的最佳设计建议:))