C#: System.Object与泛型的区别

38

我很难理解何时使用对象(装箱/拆箱)而何时使用泛型。

例如:

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

对比。

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的方法可以让我在栈内存储各种类型的对象。那么这种方式不是总是更好吗?谢谢!


请参阅相关问题 为什么不总是使用泛型 - nawfal
9个回答

45

始终使用泛型!使用对象会造成强制转换操作和值类型的装箱/拆箱。因为这些原因,泛型更快、更优雅(不需要强制转换)。而且 - 最主要的原因 - 使用泛型不会出现InvalidCastException

因此,泛型更快,并且错误在编译时可见。System.Object意味着运行时异常和强制转换,通常导致性能降低(有时明显降低)。


1
除非您需要多种类型,否则请尝试使用泛型来处理ASP.NET会话对象。 - H H
2
我的意思是泛型可以随时使用,如果你真的需要在集合中存储多个类型,你可以简单地使用类似于List<object>这样的东西。 - illegal-immigrant
2
那么,“始终使用泛型”如何呢? - H H
1
我只是想说,即使你想存储多种类型,也应该每次都使用泛型 - 你可以使用List<object>。List<object>是一种泛型集合 :)。当然,在这种情况下,你必须处理强制转换和(取消)装箱。我认为我们只是彼此理解不了。 - illegal-immigrant
4
我有点怀疑“总是”这个词……所以对我来说绝对没有使用“object”的情况? - Shai UI
显示剩余5条评论

16
很多人推荐使用泛型,但似乎他们都没有理解重点。通常不是与装箱基元类型或强制转换相关的性能损失,而是让编译器为你工作。
如果我有一个字符串列表,我希望编译器向我证明它将始终包含字符串列表。泛型就做到了这一点 - 我指定意图,编译器为我证明它。
理想情况下,我更喜欢一个更丰富的类型系统,例如,即使它是引用类型,也可以声明该类型不包含null值,但是C#目前不支持此功能。

10

尽管有时您可能希望使用非泛型集合(比如缓存),但您几乎总是拥有同构对象的集合而不是异构对象。对于同构集合,即使它是基类型或接口的变体集合,使用泛型也总是更好的选择。这将使您免于在使用结果之前将其强制转换为实际类型的麻烦。使用泛型可以使您的代码更加高效和易读,因为您可以省略执行强制转换的代码。


5
一切取决于你长期的需求。与大多数答案不同,我不会说“总是使用泛型”,因为有时您确实需要将猫和黄瓜混合在一起。尽管出于其他答案中已经给出的所有原因,例如如果您需要组合猫和狗,请创建基类Mammal并具有Stack<Mamal>,但当您真正需要支持每种可能的类型时,不要害怕使用objects,除非您虐待它们,否则它们不会咬人。 :)

4
使用“object”类型时,需要执行装箱和拆箱操作,这很快变得繁琐。使用泛型,就不需要这样做。
另外,我更愿意更具体地指出类可以使用哪种对象,并且泛型为此提供了良好的基础。为什么首先要混合不相关的数据类型?你的一个特定的堆栈示例强调了泛型优于基本的“object”数据类型的好处。
// This stack should only contain integers and not strings or floats or bools
Stack<int> intStack = new Stack<int>();
intStack.Push(1);

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


1

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

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

如果你想让它处理多种类型,你可以不使用泛型,但是会因为装箱/拆箱操作而产生一些性能损失。


“Single type” 这个术语有点误导,因为静态类型可能是多种类型的基础类型... - SoftMemes

1

如果可能的话,通常会优先选择泛型。

除了性能之外,泛型还允许您对正在处理的对象类型进行保证。

这比强制转换更受欢迎的主要原因是编译器知道对象的类型,因此它可以立即给出编译错误,而不是在某些您没有测试过的情况下才可能发生的运行时错误。


0

0

泛型并非银弹。在您的活动自然不是泛型的情况下,请使用老式的对象。其中一种情况是缓存。缓存自然可以容纳不同类型。我最近看到了这个缓存包装器的实现。

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 的用途,特别是在存储不同类型的集合时。或者当您需要基于类型消耗它们时,存在完全任意和不相关对象的组合。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接