我有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
数据库获取存储的(基本)值:
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
属性,然后对所有其他统计信息重复此操作。
参考方案
在这里,反思是显而易见的答案(并非唯一的答案)。
首先,确保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
使用字符串,则可以在数据库中引入新的统计信息,而无需更改枚举。
如果您使用枚举来避免“魔术字符串”(也许某些统计信息在硬编码引用中使用名称来引用),那么对于静态检查和IntelliSense,枚举是正确的做法。但是,您可能需要考虑为代码需要通过名称知道的统计信息保留一个枚举或只读全局变量,然后再从数据库中接受在代码中没有任何特殊状态的统计信息中的任意字符串名称。那是您今天不需要穿过的一座桥梁。
Panagiotis还指出,您可以使CharacterStats
为Dictionary<String, Stat>
的子类,并且完全避免弄乱反射。这不是一个坏主意。一个缺点是魔术字符串会再次出现:例如,如果每个字符都有Level
,Damage
等,并且代码专门与Level
交互,则将Level
设为类属性是明智的。
我有一个包含成员Predicate的类,希望在Linq表达式中使用该类:using System.Linq; class MyClass { public bool DoAllHaveSomeProperty() { return m_instrumentList.All(m_filterExpression); } private IEnumerable&…
合并List <T>和List <Optional <T >> - java鉴于: List<Integer> integers = new ArrayList<>(Arrays.asList( 10, 12 )); List<Optional<Integer>> optionalIntegers = Arrays.asList( Optional.of(5), Optional.em…
无法从ArrayList <String>转换为List <Comparable> - java当我写下面的代码时,编译器说 无法从ArrayList<String>转换为List<Comparable>private List<Comparable> get(){ return new ArrayList<String>(); } 但是当我用通配符编写返回类型时,代码会编译。private List&l…
OpenShift构建错误:无法在多模块Maven Spring启动项目的父模块中导入子模块类 - java我有一个使用spring的多模块Maven项目。通用模块类用作业务模块项目中的直接导入。我可以在本地PC上编译并成功运行它们。当我在OpenShift中部署相同的模块时,出现错误,无法在业务模块中导入通用模块类。项目结构可以总结如下:项目根 通用模块 src pom.xml 业务模块 src pom.xml pom.xml父POM:<?xml vers…
List <Dog>是List <Animal>的子类吗?为什么Java泛型不是隐式多态的? - java我对Java泛型如何处理继承/多态感到困惑。假设以下层次结构-动物(父母)狗-猫(儿童)因此,假设我有一个方法doSomething(List<Animal> animals)。根据继承和多态性的所有规则,我假设List<Dog>是List<Animal>,而List<Cat>是List<Animal&g…