我有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
属性,然后重复所有其他统计信息。
答案 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>
的子类,并避免乱用反射。这不是一个坏主意。一个缺点是魔术字符串再次出现:例如,如果每个字符都有Level
,Damage
等,并且代码专门与Level
交互,那么{{1}类属性是明智的。