我对如何实现这一目标感到有些困惑,所以我已尽我所能简化了这个问题,希望能提出一两个建议。
我有一堂课叫做生物。
class Creature
{
private string m_species;//This needs to be updated whenever any of the other variables change. Based on a list of user-defined species.
private string m_name;
private int m_numberOfLegs;
private SkinColorEnum m_skinColor;
//etc...
}
我希望能够以某种方式定义物种,然后将其与Creature实例进行比较。 (一个人的皮肤会是粉红色或黑色,兽人的皮肤会是绿色。)
为了更加困难,我还想序列化/反序列化生物实例和物种实例。这样,我就可以让编辑人员定义我的生物和物种,然后将其导入到我的实际程序(在本例中为游戏)中进行比较并找出每个生物属于哪个物种。
因此,物种的定义类似于:
class Species
{//Not actual c# syntax. Just trying to explain.
Creature.m_name;
Creature.m_numberOfLegs;
Creature.m_skinColor;
}
人类:
m_name == ?; //Don't care what the name is.
m_numberOfLegs == 2; //Must have 2 legs.
m_skinColor == (Black || Pink);//Need to be able to specify multiple possible values.
兽人:
m_name == ?; //Don't care what the name is.
m_numberOfLegs == 2; //Must have 2 legs.
m_skinColor == Green;//Need to be able to specify multiple possible values, even though we only need 1 for orc.
有没有可以执行此操作的C#构造?也许有一些使用属性的方法?遗产?我想不出任何可行的方法。
如果我将变量添加到Creature中而不是Species,则会给我一些编译器错误的功能。
用例:
1. The user starts the "EditSpeciesAndCreatures" executable.
2. The user defines a list of species. Each species has for every variable in "Creature" defined either: A set of matching values, a single value or no value(indicating it does not matter what that variable is).
3. The user defines a list of creatures. Simply setting each variable to something. Strings as strings, ints as ints, enums as enums. Nothing unusual.
4. The user saves the two lists to file. (Serializing)
5. The user starts "The Game", and the game loads the file. (Deserializing)
6. All the values in each Creature are set as they were defined in the file.
7. All the values in each Species are set as they were defined in the file.
8. A comparison is run with every creature against every Species. The first match results in Creature's m_species variable being set to that Species.
9. All creatures are now marked as their respective species.
其他用例:
10. One or more of the creatures change. Skin color, number of legs, anything.
11. A new comparison is run for these Creatures, and a new species is found, unless the changed variable did not matter to any of the defined Species.
答案 0 :(得分:1)
因此,您需要维护各种Species
实例,这些实例可以在GUI中进行编辑。并且您需要维护一组Creates
,每个集合都属于给定的Species
。然后,您有两个不同的类以及它们之间的引用:
public class Species
{
// species name, e.g. 'Human', 'Orc'
public string Name { get; set; }
public int NumberOfLegs { get; set; }
// if SkinColorEnum is a [Flags]-enum, then it can have multiple values here
public SkinColorEnum SkinColor { get; set; }
}
public class Creature
{
// the species of this creature
public Species Species { get; set; }
// creature name, e.g. 'Adam', 'Eve', or `Balogog` (an orc)
public string Name { get; set; }
// we assume only a single skin color value is used, even though SkinColorEnum were a Flags
public SkinColorEnum SkinColor { get; set; }
}
请注意,绝对不需要复制NumberOfLegs
,因为对于属于同一物种的所有生物,可以假定它们为恒定。但是,您是否需要代表例如一条腿被砍掉了,您可以使用nullable type int?
并执行以下操作:
public int NumberOfLegs
{
get => numberOfLegs ?? Species.NumberOfLegs;
set => numberOfLegs = value;
}
private int? numberOfLegs;
然后可以将游戏中的所有对象都放在单个World
类下:
public class World
{
public List<Species> Species { get; set; }
public List<Creature> Creatures { get; set; }
}
这样就很容易序列化,例如通过使用XmlSerializer
。
答案 1 :(得分:1)
好的,所以根据您的用例,我会提出这样的建议:
interface IFeature<T> where T : IEquatable<T>
{
bool IsMatch(T creatureFeature);
}
class FeatureAny<T> : IFeature<T> where T : IEquatable<T>
{
public bool IsMatch(T creatureFeature) => true;
}
class FeatureSingle<T> : IFeature<T> where T : IEquatable<T>
{
private T _feature;
public FeatureSingle(T feature) => _feature = feature;
public bool IsMatch(T creatureFeature) => _feature.Equals(creatureFeature);
}
class FeatureMany<T> : IFeature<T> where T : IEquatable<T>
{
private ISet<T> _features;
public FeatureMany(params T[] features) => _features = new HashSet<T>(features);
public bool IsMatch(T creatureFeature) => _features.Contains(creatureFeature);
}
class FeatureFactory
{
public IFeature<T> MakeFeature<T>(params T[] features) where T : IEquatable<T>
{
if(!features.Any())
{
return new FeatureAny<T>();
}
else if(features.Length == 1)
{
return new FeatureSingle<T>(features.Single());
}
else
{
return new FeatureMany<T>(features);
}
}
}
enum SkinColor
{
White,
Black,
Green
}
interface ICreature
{
string Name { get; set; }
int NumberOfLegs { get; set; }
SkinColor SkinColor { get; set; }
ISpecies Species { get; set; }
}
interface ISpecies
{
IFeature<string> Name { get; set; }
IFeature<int> NumberOfLegs { get; set; }
IFeature<SkinColor> SkinColor { get; set; }
}
现在确定每个Species
的{{1}}将会遍历已定义的种类并调用如下子程序:
Creature
物种定义示例:
static bool IsCreatureOfSpecies(ICreature creature, ISpecies species) =>
species.Name.IsMatch(creature.Name) &&
species.NumberOfLegs.IsMatch(creature.NumberOfLegs) &&
species.SkinColor.IsMatch(creature.SkinColor);
您可以通过为功能创建自定义类型并覆盖var humanSpecies = new Species
{
Name = FeatureFactory.MakeFeature(),
NumberOfLegs = FeatureFactory.MakeFeature(2),
SkinColor = FeatureFactory.MakeFeature(SkinColor.White, SkinColor.Black)
};
方法来影响比较。序列化和反序列化应该简单明了,并为Equals
的每个实现定义。 IFeature
方法是唯一可以磨磨齿轮的方法,因为每次更改IsCreatureOfSpecies
定义时都需要将其填满。另外,ISpecies
的定义取决于ICreature
(它们必须具有互补的字段)。
对于我而言,很难找到一种在编译时会警告您的解决方案,但是您可以使用反射来解决这个问题。首先,您可以编写ISpecies
方法,以便对IsCreatureOfSpecies
中类型为ISpecies
的所有属性进行迭代,并在IFeature<T>
中找到具有相同名称的属性。其次,您可以通过单元测试来保护自己。只需使用反射确保接口互补,并全面测试ICreature
。