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

前端之家收集整理的这篇文章主要介绍了c# – 如何表示具有相同行为的不同实体?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的域模型中有几个不同的实体(动物物种,比方说),每个实体都有一些属性.实体是只读的(它们在应用程序生命周期内不会改变状态)并且它们具有相同的行为(仅由属性值不同).

如何在代码中实现这样的实体?

不成功的尝试:

枚举

我尝试了这样的枚举:

enum Animals {
    Frog,Duck,Otter,Fish  
}

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

子类

我还想过每个动物类型在哪里是公共基础抽象类的子类.但是,对于所有动物来说,Swim()的实现是相同的,因此它没有多大意义,可串行化现在是一个大问题.由于我们代表一种动物类型(物种,如果你愿意),每个应用程序应该有一个子类的实例,当我们使用序列化时,这很难维护.

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),你可以轻松添加所有已定义动物的静态列表,你有一个合理的地方可以实现Swim().通过使财产制定者受到保护可以实现不变性.但是存在一个主要问题:它打破了可串行化.序列化的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类型,它相当广泛.也许你以后将它分成鲨鱼和金鱼,并决定你所有的旧鱼类应该被认为是金鱼.通过这种序列化的分离,您现在可以检查任何旧Fish并将它们转换为Goldfish,而直接序列化将导致异常,因为Fish不再存在.

猜你在找的C#相关文章