c#中的设计问题:泛型或多态性或两者兼而有之

本文关键字:多态性 兼而有之 泛型 问题 | 更新日期: 2023-09-27 17:53:55

我需要一些帮助,我有一个设计问题。我想实现的是:

我有一个名为Document的主类。这个类有一个属性类的列表。这些属性类有一些共同的属性,如Identity和Name,但它们在一个我称为Value的属性上有所不同。对于每个不同的属性类,值类型是不同的,可以是字符串、整数、列表、日期时间、浮点数、列表以及由几个属性组成的类。一个例子是类PairAttribute,它有两个属性:Title和Description。

我试图实现的是Attribute子类的Value属性的类型安全,并且这些子类应该能够添加到Document类中的Attribute列表中。我本可以只创建一个具有object类型的Value属性的Attribute类,但这正是我在这里要避免的。

公共属性(身份和名称)应该放在一个基类中,我猜,我们叫它AttributeBase类。但我想有一个子类,说StringAttribute,其中的值属性是类型字符串,一个IntegerAttribute类,其中的值属性是类型Integer,一个StringListAttribute,其中的值属性是类型列表,一个PairAttribute类,其中的值是一个类与几个属性,等

你知道我怎么实现这个吗?这是一个解决方案,我应该去还是它是一个更好的方法来解决这种类型的安全问题?我将欣赏代码示例来澄清:)

c#中的设计问题:泛型或多态性或两者兼而有之

您不指定语言,但是术语"泛型"(如Java和c#所使用的)所描述的特性在其他语言(如ML和Haskell)中通常称为"参数多态性"。相反,在Java和c#中,"多态性"的一般含义实际上更准确地称为"子类型多态性"。

关键是,无论你是使用子类型多态性还是参数多态性,你的问题都需要多态性,所以我认为你是在正确的轨道上。

问题归结为:参数多态性何时优于子类型多态性?答案其实很简单:当它需要你编写更少的代码时。

所以,我建议你对两种方法都进行原型化,然后看看哪一种方法更简单,更容易理解代码。

你可以传递一个泛型属性而不是继承/方法多态性,但你需要将它们存储在一些列表中,为此你需要一个接口,因为。net中的数据存储不能是未定义的泛型类型的集合,如果你定义它们,你就不能在里面混合类型:

    public interface IAttribute {
        string Identity { get; set; }
        string Name { get; set; }
        T GetValue<T>();
    }
    public class Attribute<T> : IAttribute
    {
        public string Identity { get; set; }
        public string Name { get; set; }
        public T Value { get; set; }
        public Tret GetValue<Tret>() {
            return (Tret)(Object)Value;
        }
    }
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            List<IAttribute> lst = new List<IAttribute>();
            Attribute<string> attr1 = new Attribute<string>();
            attr1.Value = "test";
            Attribute<int> attr2 = new Attribute<int>();
            attr2.Value = 2;
            lst.Add(attr1);
            lst.Add(attr2);
            string attr1val = lst[0].GetValue<string>();
            int attr2val = lst[1].GetValue<int>();
        }
    }

(Tret)(Object)实际上不改变类型,它只是将T框起来,而(Tret)在不使用中间变量的情况下将值打开。如果在调用GetValue<type>()时错过了正确的类型,这当然会失败。即使发送的是兼容类型,如Value is integer,并且您执行GetValue<double>() ->,因为不允许将整数拆箱成双精度类型。

装箱/拆箱不如强制类型转换快,但它确保了类型安全,而且据我所知,没有其他方法可以将泛型与编译时已知的接口一起使用。

所以这应该是类型安全的…而且不需要很多代码