【问题标题】:How to implement a class to access objects of several different types in C#?如何在 C# 中实现一个类来访问几种不同类型的对象?
【发布时间】:2010-06-04 18:26:40
【问题描述】:

我正在尝试实现一个类来访问不同类型的项目,以类似于数据库行的方式。

但是,我有两种不同的想法,不知道该选择哪一种:

设计 1

public enum ObjectTypeA
{
    Undefined,
    Integer,
    Float
}

public class MyObjectA
{
    private object val;

    public ObjectTypeA Type
    {
        get;
        private set;
    }

    public int Integer
    {
        get
        {
            if (Type != ObjectTypeA.Integer) throw new Exception();
            return (int)val;
        }
        set
        {
            Type = ObjectTypeA.Integer;
            val = value;
        }
    }

    public float Float
    {
        get
        {
            if (Type != ObjectTypeA.Float) throw new Exception();
            return (float)val;
        }
        set
        {
            Type = ObjectTypeA.Float;
            val = value;
        }
    }
}
  • 可能的编译时检查更少。
  • 不能使用 is 运算符、GetType() 等(重新发明类型系统)。
  • 值类型的装箱和拆箱。
  • 可以被其他类继承(例如,我可以使用继承创建一个“命名对象”)。

设计 2

public abstract class MyObjectB
{
}

public class MyIntegerB : MyObjectB
{
    public int Value
    {
        get;
        set;
    }

    public MyIntegerB(int _value)
    {
        Value = _value;
    }
}

public class MyFloatB : MyObjectB
{
    public float Value
    {
        get;
        set;
    }

    public MyFloatB(float _value)
    {
        Value = _value;
    }
}
  • 实现更短更简单。
  • 使用起来非常冗长(强制转换)。

性能并不重要,但仍然很重要,因为要存储的大多数对象都是整数或浮点数,因此装箱开销很重要。

类将只包含值,而不是依赖于类型等的方法,因此解决方案是否使用继承无关紧要。

重要提示:要求之一是可能有两种类型使用相同的底层类型(例如,从 MyObjectB 派生的两个类可能使用 int 作为值) ,因此可能无法使用 object 或泛型。

关于使用哪种设计或其他不同设计的任何建议?

编辑: 我不喜欢第二个的原因是因为它使用起来非常冗长:

MyObjectB objB = new MyIntegerB(12);
Console.WriteLine(((MyIntegerB)objB).Value);

而且因为我不能继承它来创建类似“命名对象”的东西,所以我必须将MyObjectB附加到该类中,并且用法更加冗长。

【问题讨论】:

  • 第一个选项似乎是实现 C 风格联合的糟糕尝试。
  • 您这里的要求非常模糊,这使得问题难以回答。这会引发各种危险信号,说明您实际想要完成的工作,以及您认为需要这样做的原因。
  • 如果你想支持不止一种类型的对象,只需使用Nullable<T> 并在运行时测试/转换类型就可以了。

标签: c# dynamic


【解决方案1】:

我不明白你为什么不在这里使用泛型。更强烈:我不明白你为什么需要这个:看起来Nullable<T> 可以很好地涵盖你所有的用例。如果没有,一般地实现它是微不足道的:

public class ValueWrapper<T>
{
    public T Value
    {
        get;
        private set;
    }

    public Type WrappedType
    {
        get { return typeof(T); }
    }
}

public MySpecialInt : ValueWrapper<int>
{
    /* etc */
}

【讨论】:

  • +1 表示Nullable&lt;T&gt;。如果所有 OP 试图将原语包装到引用类型中,Nullable(或至少 某种 形式的泛型)是理想的。
  • 这是您正在寻找的答案,即使您认为它不是。除非您遗漏了某些内容,否则它确实符合您的要求。您似乎误解了泛型,或者忽略了一些重要的事情。
  • 总是答案。你怎么写List&lt;ValueWrapper&lt;?&gt;&gt; list = GetAllValues(); foreach (item) switch (item.WrappedType) ...。看到 ?
  • @Firo,没有直接的方法在 C# 中实现异构集合,除非类型共享一个基类。如果那是 OP 需要的,那么他应该这么说。
【解决方案2】:

为什么不使用泛型?

public abstract class MyObjectB<T>
{
    public T Value
    {
        get;
        set;
    }

    public MyObjectB(T _value)
    {
        Value = _value;
    }
}

此时您只需要一堂课。只是以不同的方式实例化它:

var myObj = new MyObjectB&lt;Int&gt;(1);

var myObj = new MyObjectB&lt;Float&gt;(0.012);

【讨论】:

  • 您的语法不正确。您需要在类名后面加上&lt;T&gt;。此外,您必须在构造过程中包含泛型类型:new MyObjectB&lt;int&gt;(1)new MyObjectB&lt;double&gt;(0.12)
  • 我的语法是正确的...stackoverflow 过滤掉了泛型标签。已更新以解决此问题。
【解决方案3】:

我知道你提到不想处理装箱和拆箱,但我仍然认为 Generic 类是你最好的选择。

public class MyObject<T>
{
    public MyObject(T t) {
        Value = t;
    }

    public T Value { get; set; }
}

编辑:

其中一个要求是 可能是两种使用相同的类型 基础类型(例如两个类 从 MyObjectB 派生的可以使用 int 作为 值),所以使用对象或 泛型可能是不可能的。

这仅适用于您扩展课程的情况。如果您将类包装起来,即创建一个MyObject&lt;int&gt; 并访问其Value 属性,而不是对其进行子类化,则没有问题。

话虽如此,如果你想继承一个泛型类,子类也需要是一个泛型类。

【讨论】:

    【解决方案4】:

    你考虑过泛型吗?

    public class MyObjectA<T> { 
    
      public T Value { 
          get; set;
      } 
    
    }
    

    【讨论】:

      【解决方案5】:

      我编写了一个类似的类,它可以保存ClassX 的单个实例或ClassX 的数组。诀窍是它可以在运行时改变,所以泛型是不够的,但我仍然希望它在所有情况下都是强类型的。听起来这与您在此处尝试完成的任务相似。

      我选择了第一个选项,原因如下:在可能的情况下,我将复杂性封装在一个类中以使该类更易于使用。类应该封装调用者的复杂性,并对其进行调用更简洁。如果使用MyObjectB 会使您的代码更加冗长,那么我认为这不是正确的答案。

      【讨论】:

      • 仅供参考,为什么 ClassX 不总是保存一个数组,它可能是一个包含 1 个元素的数组。看起来像是矫枉过正(和滥用 OOP),但我不知道这里的细节。
      • @drharris:因为单个项目是常见的情况,我不想对每个需要单个项目的函数添加检查以确保数组只有一个元素。有了这个解决方案,我可以依靠类型系统来捕捉我犯的任何错误。不,它不漂亮,但它可以很好地满足我的需要。由于单个项目是常见的情况,因此也会误导代码维护人员在任何地方看到一个数组。
      • 感谢您的更新。几年前,我在以前的工作中做过非常相似的事情,并被告知仅适用于一般情况(始终为数组,无论长度如何)。很高兴听到有人成功地使用了这种模式。
      【解决方案6】:

      如果您需要异构集合,那么就可以了。

      public enum ObjectTypeA
      {
          Undefined,
          Integer,
          Float
      }
      
      public class MyObjectA
      {
          public MyObjectA(object value) : this(value, InfereType(value))
          { }
      
          public MyObjectA(object value, ObjectTypeA type)
          {
              Value = value;
              Type = type;
          }
      
          public object Value { get; private set; }
      
          public ObjectTypeA Type
          {
              get;
              private set;
          }
      
          public T ValueAs<T>()
          {
              return (T)Value;
          }
      }
      

      然后像这样使用它

      List<MyObjectA> list = GetAllValues();
      foreach (var item in list)
      {
          switch (item.WrappedType)
          {
              case MyObjecttypeA.Float:
                  float f = item.ValueAs<float>();
                  // do something with float
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-08-19
        • 2021-06-17
        • 2020-02-17
        • 1970-01-01
        • 1970-01-01
        • 2018-03-02
        • 1970-01-01
        相关资源
        最近更新 更多