从IEnumerable <t>的命名值

时间:2016-03-29 16:20:05

标签: c# linq sqlite

我有2个函数返回IEnumerable<Stat>

此函数返回属于CharacterStats类的所有单个属性:

private IEnumerable<Stat> GetAllStats()
{
    yield return Level;
    yield return Health;
    yield return Damage;
    yield return Defense;
    //several others
    yield return LifePerSecond;
}

此函数从我新添加的sqlite db:

中获取存储的(基本)值
private IEnumerable<Stat> GetBaseStatsFromDB()
{
    //connection code ...

    while (rdr.Read())
    {
        yield return new Stat
        {
            Name = (Enums.StatName)Enum.Parse(typeof(Enums.StatName), rdr.GetString(1)),
            MinValue = rdr.GetInt32(2),
            MaxValue = rdr.GetInt32(3),
            CurrentValue = rdr.GetInt32(4)
        };
    }

    //close connection
}

我想要做的是返回CharacterStats的新实例,其中每个Stat都从数据库初始化并分配给实例,如:

GetAllStats.ToList().ForEach( //apply matching stat from GetBaseStatsFromDB());

我能够从数据库中成功获取值并从每个中创建一个新的Stat对象。但是,我无法将这些值应用于CharacterStats类。

目前有大约16个统计数据,当我继续构建这个项目时,可能会添加/删除这些数据,所以理想情况下我想找到一个方法,这个方法会随着这个列表的变化而继续工作。

如何使用GetBaseStatsFromDB()方法将CharacterStats的结果应用于GetAllStats()上的所有媒体资源?

编辑澄清:

CharacterStats是一个包含生成的不同Stat的类。例如:

public class CharacterStats
{
    public Stat Level;
    public Stat Name;
    ...
    public Stat LifePerSecond;
}

GetAllStats()是一个用于一次枚举这些属性的函数(如列表)

GetBaseStatsFromDB()是我尝试从数据库初始化这些值。我想要做的是在CharacterStats上使用每个属性并应用GetBaseStatsFromDB()中的匹配统计信息。

所以... GetBaseStatsFromDB()会返回(除了其他人):

Stat
{
    Name = "Level",
    MinValue = 1,
    MaxValue = 50,
    CurrentValue = 1
}

我想将结果应用到Stat:Level类的CharacterStats属性,然后重复所有其他统计信息。

1 个答案:

答案 0 :(得分:2)

反思是明显的答案(不是唯一的答案)。

首先,确保Enums.StatName的值与CharacterStats上的属性名称相同(我猜你还是这样做,因为很明显)。

//  Instance method of CharacterStats
public void SetStats(IEnumerable<Stat> stats)
{
    var cstype = this.GetType();
    foreach (var stat in stats) {
        var prop = cstype.GetProperty(stat.Name.ToString());
        prop.SetValue(this, stat);
    }
}

//  Toss in a constructor too. 
public CharacterStats(IEnumerable<Stat> stats)
{
    SetStats(stats);
}

//  And a factory method
public static FromStats(IEnumerable<Stat> stats)
{
    return new CharacterStats(stats);
}

像这样使用:

var stats = new CharacterStats(GetBaseStatsFromDB());
var stats2 = CharacterStats.FromStats(GetBaseStatsFromDB());

//  later on, maybe you want to copy stats from one to another... 
stats.SetStats(stats2.GetStats());

但是如果我们可以共享Stat个实例(并且此代码中没有任何内容可以阻止这种情况),那么在复制它们时克隆Stat实例会更安全:

public Stat() { ... }
public Stat(Stat copyMe)
{
    this.Name = copyMe.Name;
    this.MinValue = copyMe.MinValue;
    this.MaxValue = copyMe.MaxValue;
    this.CurrentValue = copyMe.CurrentValue;
}

......然后......

//  This version is much safer. 
public void SetStats(IEnumerable<Stat> stats)
{
    var cstype = this.GetType();
    foreach (var stat in stats) {
        var prop = cstype.GetProperty(stat.Name.ToString());
        prop.SetValue(this, new Stat(stat));
    }
}

@PanagiotisKanavos指出(至少根据我们已经看到的情况),Enums.StatName枚举根本不是必需的。如果您只使用Name的字符串,则可以在DB中引入新的统计信息,而无需更改枚举。

如果您正在使用枚举来避免&#34;魔术字符串&#34; (可能在硬编码引用中通过名称引用某些统计信息),然后对于静态检查和IntelliSense,枚举是正确的。但是您可能想要考虑为代码需要知道的统计信息保留枚举或只读全局变量,然后接受数据库中的任意字符串名称,以获取在代码中没有任何特殊状态的统计信息。这是你今天不需要穿越的桥梁。

Panagiotis还指出,你可以使CharacterStats成为Dictionary<String, Stat>的子类,并避免乱用反射。这不是一个坏主意。一个缺点是魔术字符串再次出现:例如,如果每个字符都有LevelDamage等,并且代码专门与Level交互,那么{{1}类属性是明智的。