【问题标题】:Design issue in C#: Generics or polymorphism or both?C# 中的设计问题:泛型或多态性或两者兼而有之?
【发布时间】:2011-04-30 05:19:12
【问题描述】:

我需要一些帮助来解决我遇到的设计问题。我试图实现的是:

我有一个名为 Document 的主类。这个类有一个属性类列表。这些 Attribute 类有一些共同的属性,例如 Identity 和 Name,但它们的不同之处在于我称之为 Value 的一个属性。每个不同的 Attribute 类的 Value 类型都不同,可以是 string、integer、List、DateTime、float、List 以及由多个属性组成的类。一个例子是我称之为 PairAttribute 的类,它有 2 个属性:Title 和 Description。

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

我猜公共属性(身份和名称)应该放在一个基类中,我们称之为 AttributeBase 类。但是我想要一个子类,比如 StringAttribute,其中 Value 属性是字符串类型,一个 IntegerAttribute 类,其中 Value 属性是 Integer 类型,一个 StringListAttribute,其中 Value 属性是 List 类型,一个 PairAttribute 类,其中 Value是一个具有多个属性等的类

你知道我该如何实现吗?这是我应该采用的解决方案,还是解决此类安全问题的更好方法?我会感谢代码示例进行澄清:)

【问题讨论】:

    标签: c# generics polymorphism


    【解决方案1】:

    您无需指定语言,但术语“泛型”(Java 和 C# 使用)所描述的特性在其他语言(如 ML 和 Haskell)中通常称为“parametric polymorphism”。反之,“polymorphism”在 Java 和 C# 中的常见含义实际上更准确地称为“subtype polymorphism”。

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

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

    所以,我建议您对这两种方法进行原型设计,然后看看哪一种方法会导致更简单、更易于理解的代码。那就这样吧。

    【讨论】:

      【解决方案2】:

      您可以使用通用属性而不是继承/方法多态性来传递,但是您需要将它们存储在某个列表中,为此您需要一个接口,因为 .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&lt;type&gt;() 时错过了正确的类型,这当然会失败。即使像 Value is integer 这样发送兼容类型,而您也这样做 GetValue&lt;double&gt;() -> 因为不允许将整数拆箱成双精度。

      装箱/拆箱不如强制转换快,但它确保了类型安全性,据我所知,没有办法以其他方式使用具有编译时已知接口的泛型。

      所以这应该是类型安全的......并且没有大量代码。

      【讨论】:

        猜你喜欢
        • 2011-06-17
        • 2015-10-14
        • 2017-12-28
        • 1970-01-01
        • 1970-01-01
        • 2013-11-14
        • 1970-01-01
        • 2018-12-01
        • 2020-02-04
        相关资源
        最近更新 更多