【问题标题】:C#: System.Object vs GenericsC#:System.Object 与泛型
【发布时间】:2010-12-12 21:09:07
【问题描述】:

我很难理解何时使用 Object(装箱/拆箱)与何时使用泛型。

例如:

public class Stack 
{
    int position;
    object[] data = new object[10];
    public void Push (object o) { data[position++] = o; }
    public object Pop() { return data[--position]; }
}

VS.

public class Stack<T>
{ 
  int position; 
  T[] data = new T[100]; 
  public void Push(T obj)  {data[position++] = obj; }
  public T Pop() { return data[--position]; }
 }

我应该在什么条件下使用哪一个?似乎使用 System.Object 方式,我可以拥有当前存在于我的堆栈中的各种类型的对象。那么这不是总是更可取吗?谢谢!

【问题讨论】:

  • 在 Canines 集合中添加猫有什么意义?这种灵活性只会产生死猫。
  • 也看到这个相关问题why-not-always-use-generics

标签: c# generics object


【解决方案1】:

总是使用泛型!在强制转换操作和值类型的装箱/拆箱中使用对象的结果。由于这些原因,泛型更快、更优雅(没有强制转换)。而且 - 主要原因 - 你不会使用泛型获得 InvalidCastExceptions。

因此,泛型更快,并且错误在编译时可见。 System.Object 表示运行时异常和强制转换通常会导致性能下降(有时会低很多)。

【讨论】:

  • 除非您需要多种类型。尝试用泛型做 ASP.NET Session 对象。
  • 我的意思是泛型可以每次都使用,如果你真的需要在你的集合中存储多种类型,你可以简单地使用类似 List
  • 那么“始终使用泛型”会在哪里留下?
  • 我的意思是,即使你想存储多种类型,每次都应该使用泛型——你可以使用 List。 List 是通用集合:)。当然,在这种情况下,您必须处理铸造和(取消)装箱。我想我们只是不了解对方
  • 我对 ALWAYS 有点怀疑……所以我绝对不能使用“对象”?
【解决方案2】:

很多人都建议使用泛型,但看起来他们都没有抓住重点。这通常与装箱原始类型或强制转换相关的性能损失无关,而是让编译器为您工作。

如果我有一个字符串列表,我希望编译器向我证明它总是包含一个字符串列表。泛型就是这样做的——我指定意图,编译器为我证明。

理想情况下,我更喜欢一个更丰富的类型系统,例如,你可以说一个类型(即使它是一个引用类型)不能包含空值,但遗憾的是 C# 目前不提供它。

【讨论】:

    【解决方案3】:

    虽然有时您会想要使用非泛型集合(例如缓存),但您几乎总是拥有同质对象而不是异构对象的集合。对于同质集合,即使它是基类型或接口的变体的集合,使用泛型总是更好。这将使您不必在使用它之前将结果转换为真实类型。使用泛型可以让您的代码更加高效和易读,因为您可以省略代码来进行强制转换。

    【讨论】:

      【解决方案4】:

      从长远来看,这一切都取决于您的需求。

      与这里的大多数答案不同,我不会说“始终使用泛型”,因为有时您确实需要将猫与黄瓜混合使用。

      出于其他答案中已经给出的所有原因,请尽量坚持使用泛型,例如,如果您需要将猫和狗结合起来创建基类 Mammal 并拥有Stack&lt;Mamal&gt;

      但是当你真的需要支持所有可能的类型时,不要害怕使用对象,除非你虐待它们,否则它们不会咬人。 :)

      【讨论】:

        【解决方案5】:

        使用object 类型,正如您所说,您需要执行装箱和拆箱,这很快就会变得乏味。有了泛型,就没有必要了。

        另外,我宁愿更具体地说明类可以使用的对象类型,而泛型为此提供了很好的基础。为什么首先要混合不相关的数据类型?您的堆栈的特定示例强调了泛型相对于基本 object 数据类型的好处。

        // This stack should only contain integers and not strings or floats or bools
        Stack<int> intStack = new Stack<int>();
        intStack.Push(1);
        

        请记住,您可以使用泛型指定接口,以便您的类可以与许多不同类的对象进行交互,前提是它们都实现了相同的接口。

        【讨论】:

          【解决方案6】:

          当您希望结构处理单一类型时,请使用泛型。例如,如果你想要一个字符串集合,你会想要实例化一个强类型的字符串列表,如下所示:

          List<string> myStrings = new List<string>();
          

          如果您希望它处理多种类型,则可以不使用泛型,但装箱/拆箱操作会对性能造成小的影响。

          【讨论】:

          • “单一类型”有点误导,因为静态类型可能是许多类型的基类型...
          【解决方案7】:

          如果可能,总是首选泛型。

          除了性能之外,泛型还允许您对正在使用的对象类型做出保证。

          这优于强制转换的主要原因是编译器知道对象是什么类型,因此它可以为您提供立即发现的编译错误,而不是可能仅在您没有的某些情况下发生的运行时错误' t 检验。

          【讨论】:

            【解决方案8】:

            泛型不是金锤。如果您的活动自然是非通用的,请使用良好的旧对象。一种这样的情况 - 缓存。缓存自然可以容纳不同的类型。我最近看到了这个缓存包装器的实现

            void AddCacheItem<T>(string key, T item, int duration, ICacheItemExpiration expiration)
            {
                . . . . . . .
                CacheManager.Add(cacheKey, item, ..... 
            }
            

            问题:如果CacheManager 接受对象怎么办?

            然后Get 发生了真正的破坏

            public virtual T GetCacheItem<T>(string cacheKey)
            {
                return (T)CacheManager.GetData(cacheKey); // <-- problem code
            }
            

            上面的问题是值类型会崩溃。

            我通过添加这个来修改方法

            public T GetCacheItem<T>(string cacheKey) where T : class
            

            因为我喜欢这样做的想法

            var x = GetCacheItem<Person>("X")?
            string name = x?.FullName;
            

            但我添加了新方法,它也允许采用值类型

            public object GetCacheItem(string cacheKey)
            

            归根结底,object 有用法,尤其是在将不同类型存储在集合中时。或者,当您需要根据类型使用它们时,可以存在完全任意且不相关的对象的组合。

            【讨论】:

              猜你喜欢
              • 2012-06-28
              • 1970-01-01
              • 1970-01-01
              • 2019-02-11
              • 2010-09-26
              • 1970-01-01
              • 2010-11-03
              • 2016-06-13
              • 2019-10-10
              相关资源
              最近更新 更多