gpt4 book ai didi

c# - 如何表示具有相同行为的不同实体?

转载 作者:太空狗 更新时间:2023-10-29 20:21:18 25 4
gpt4 key购买 nike

我的域模型中有几个不同的实体(比方说动物物种),每个实体都有一些属性。这些实体是只读的(它们在应用程序生命周期内不会更改状态)并且它们具有相同的行为(仅属性值不同)。

如何在代码中实现这些实体?

不成功的尝试:

枚举

我试过这样的枚举:

enum Animals {
Frog,
Duck,
Otter,
Fish
}

而其他代码片段会打开枚举。然而,这会导致丑陋的切换代码,分散逻辑和 problems with comboboxes。 .没有很好的方法来列出所有可能的动物。不过,序列化效果很好。

子类

我还考虑过每个动物类型在哪里是公共(public)基础抽象类的子类。不过,Swim() 的实现对所有 Animals 都是相同的,因此它没有什么意义,而且可序列化现在是一个大问题。由于我们代表一种动物类型(物种,如果你愿意的话),每个应用程序应该有一个子类实例,当我们使用序列化时,这很难维护而且很奇怪。

public abstract class AnimalBase {
string Name { get; set; } // user-readable
double Weight { get; set; }
Habitat Habitat { get; set; }
public void Swim(); { /* swim implementation; the same for all animals but depends uses the value of Weight */ }
}

public class Otter: AnimalBase{
public Otter() {
Name = "Otter";
Weight = 10;
Habitat = "North America";
}
}

// ... and so on

简直糟透了。

静态字段

This blog post给了我一个解决方案的想法,其中每个选项都是类型内静态定义的字段,如下所示:

public class Animal {
public static readonly Animal Otter =
new Animal
{ Name="Otter", Weight = 10, Habitat = "North America"}
// the rest of the animals...

public string Name { get; set; } // user-readable
public double Weight { get; set; }
public Habitat Habitat { get; set; }

public void Swim();

那就太好了:您可以像枚举一样使用它 (AnimalType = Animal.Otter),您可以轻松添加所有已定义动物的静态列表,您有一个合理的地方来实现 游泳()。不变性可以通过保护属性 setter 来实现。但是,有一个主要问题:它破坏了可序列化性。一个序列化的 Animal 必须保存它的所有属性,并且在反序列化时它会创建一个新的 Animal 实例,这是我想避免的事情。

有没有一种简单的方法可以让第三次尝试成功?对于实现这种模型还有更多建议吗?

最佳答案

如果序列化有问题,您可以随时将应用程序代码与序列化代码分开。也就是说,放置转换为序列化状态/从序列化状态转换的转换类。序列化实例可以公开任何所需的空构造函数和属性,它们唯一的工作就是序列化状态。同时,您的应用程序逻辑使用不可序列化、不可变的对象。这样,您就不会将序列化问题与逻辑问题混为一谈,这会带来许多您发现的缺点。

编辑:这是一些示例代码:

public class Animal 
{
public string Name { get; private set; }
public double Weight { get; private set; }
public Habitat Habitat { get; private set; }

internal Animal(string name, double weight, Habitat habitat)
{
this.Name = name;
this.Weight = weight;
this.Habitat = habitat;
}

public void Swim();
}

public class SerializableAnimal
{
public string Name { get; set; }
public double Weight { get; set; }
public SerializableHabitat Habitat { get; set; } //assuming the "Habitat" class is also immutable
}

public static class AnimalSerializer
{
public static SerializableAnimal CreateSerializable(Animal animal)
{
return new SerializableAnimal {Name=animal.Name, Weight=animal.Weight, Habitat=HabitatSerializer.CreateSerializable(animal.Habitat)};
}

public static Animal CreateFromSerialized(SerializableAnimal serialized)
{
return new Animal(serialized.Name, serialized.Weight, HabitatSerializer.CreateFromSerialized(serialized.Habitat));
}

//or if you're using your "Static fields" design, you can switch/case on the name
public static Animal CreateFromSerialized(SerializableAnimal serialized)
{
switch (serialized.Name)
{
case "Otter" :
return Animal.Otter
}

return null; //or throw exception
}
}

那么您的序列化应用程序逻辑可能类似于:

Animal myAnimal = new Animal("Otter", 10, "North America");
Animal myOtherAnimal = Animal.Duck; //static fields example

SerializableAnimal serializable = AnimalSerializer.CreateSerializable(myAnimal);
string xml = XmlSerialize(serializable);
SerializableAnimal deserialized = XmlDeserializer<SerializableAnimal>(xml);

Animal myAnimal = AnimalSerializer.CreateFromSerialized(deserialized);

重申一下,SerializableAnimal 类和用法用于需要序列化/反序列化的应用程序的最后一层。 其他一切 都对不可变的 Animal 类不利。

EDITx2:这种托管分离的另一个主要好处是您可以处理代码中的遗留更改。例如,您有一个非常广泛的 Fish 类型。也许您稍后将其拆分为 SharkGoldfish 并决定所有旧的 Fish 类型都应被视为 Goldfish。通过这种序列化分离,您现在可以检查任何旧的 Fish 并将它们转换为 Goldfish,而直接序列化会导致异常,因为 Fish 不再存在。

关于c# - 如何表示具有相同行为的不同实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10866271/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com