C#:System.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 方式,我可以在我的堆栈中拥有当前存在的各种类型的对象。那么这不是总是更好吗?谢谢!
I'm having a hard time understanding when to use Object (boxing/unboxing) vs when to use generics.
For example:
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]; }
}
Which one should I use and under what conditions? It seems like with the System.Object way I can have objects of all sorts of types currently living within my Stack. So wouldn't this be always preferable? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
始终使用泛型!在值类型的转换操作和装箱/拆箱中使用对象的结果。由于这些原因,泛型更快、更优雅(无需转换)。而且 - 主要原因 - 使用泛型你不会得到
InvalidCastException
。因此,泛型速度更快,并且错误在编译时可见。 System.Object 意味着运行时异常和转换,这通常会导致性能降低(有时会低很多)。
Always use generics! Using object's results in cast operations and boxing/unboxing of value-types. Because of these reasons generics are faster and more elegant (no casting). And - the main reason - you won't get
InvalidCastException
s using generics.So, generics are faster and errors are visible at compile-time.
System.Object
means runtime exceptions and casting which in general results in lower performance (sometimes MUCH lower).很多人都建议使用泛型,但看起来他们都没有抓住要点。这通常与装箱原始类型或转换相关的性能影响无关,而是让编译器为您工作。
如果我有一个字符串列表,我希望编译器向我证明它始终包含一个字符串列表。泛型就是这样做的——我指定意图,编译器为我证明这一点。
理想情况下,我更喜欢更丰富的类型系统,例如,您可以说类型(即使它是引用类型)不能包含 null 值,但遗憾的是 C# 目前不提供这一点。
A lot of people have recommended using generics, but it looks like they all miss the point. It's often not about the performance hit related to boxing primitive types or casting, it's about getting the compiler to work for you.
If I have a list of strings, I want the compiler to prove to me that it will always contain a list of strings. Generics does just that - I specify the intent, and the compiler proves it for me.
Ideally, I would prefer an even richer type system where you could say for example that a type (even if it was a reference type) could not contain null values, but C# does unfortunately not currently offer that.
虽然有时您想要使用非泛型集合(例如,考虑缓存),但您几乎总是拥有同质对象的集合,而不是异质对象的集合。对于同质集合,即使它是基类型或接口变体的集合,使用泛型总是更好。这将使您不必在使用结果之前将其转换为真实类型。使用泛型可以使您的代码更加高效和可读,因为您可以省略进行转换的代码。
While there are times when you will want to use a non-generic collection (think caching, for instance), you almost always have collections of homogenous objects not heterogenous objects. For a homogenous collection, even if it is a collection of variants of base type or interface, it's always better to use generics. This will save you from having to cast the result as the real type before you can use it. Using generics makes your code more efficient and readable because you can omit the code to do the cast.
这完全取决于您的长期需求。
与这里的大多数答案不同,我不会说“始终使用仿制药”,因为有时您确实需要将猫与黄瓜混合。
无论如何,出于其他答案中已经给出的所有原因,请尝试坚持使用泛型,例如,如果您需要组合猫和狗创建基类 Mammal 并拥有
Stack
。但是,当您确实需要支持每种可能的类型时,不要害怕使用对象,除非您虐待它们,否则它们不会咬人。 :)
It all depends on what you need in the long run.
Unlike most answers here, I won't say "always use generics" because sometimes you do need to mix cats with cucumbers.
By all means, try to stick with generics for all the reasons already given in the other answers, for example if you need to combine cats and dogs create base class Mammal and have
Stack<Mamal>
.But when you really need to support every possible type, don't be afraid to use objects, they don't bite unless you're mistreating them. :)
对于
object
类型,正如您所说,您需要执行装箱和拆箱,这很快就会变得乏味。对于泛型,就没有必要了。另外,我宁愿更具体地说明类可以使用什么类型的对象,而泛型为此提供了很好的基础。为什么首先要混合不相关的数据类型?您的堆栈的特定示例强调了泛型相对于基本
object
数据类型的优势。请记住,使用泛型,您可以指定接口,以便您的类可以与许多不同类的对象进行交互,前提是它们都实现相同的接口。
With the
object
type, as you say you need to perform boxing and unboxing, which gets tedious very quickly. With generics, there's no need for that.Also, I'd rather be more specific as to what kind of objects a class can work with and generics provides a great basis for that. Why mix unrelated data types in the first place? Your particular example of a stack emphasizes the benefit of generics over the basic
object
data type.Remember that with generics you can specify interfaces so your class can interact with objects of many different classes, provided they all implement the same interface.
当您希望结构处理单一类型时,请使用泛型。例如,如果您想要一个字符串集合,您需要实例化一个强类型的字符串列表,如下所示:
如果您希望它处理多种类型,您可以不使用泛型,但装箱/拆箱操作会对性能造成轻微影响。
Use generics when you want your structure to handle a single type. For example, if you wanted a collection of strings you would want to instantiate a strongly typed List of strings like so:
If you want it to handle multiple types you can do without generics but you will incur a small performance hit for boxing/unboxing operations.
如果可能的话,泛型总是首选。
除了性能之外,泛型还允许您对正在使用的对象的类型做出保证。
这优于强制转换的主要原因是编译器知道对象是什么类型,因此它可以为您提供立即发现的编译错误,而不是仅在您未测试的某些情况下可能发生的运行时错误。
Generics are always preferred if possible.
Aside from performance, Generics allow you to make guarantees about the types of objects that you're working with.
The main reason this is preferred to casting is that the compiler knows what type the object is, and so it can give you compile errors that you find right away instead of runtime errors that might only happen under certain scenarios that you didn't test.
仿制药不是金锤子。如果您的活动本质上是非通用的,请使用好的旧对象。其中一种情况就是缓存。缓存自然可以容纳不同的类型。我最近看到了缓存包装器的实现
问题:如果
CacheManager
采用对象,那么有什么用?然后,
Get
中出现了真正的破坏。上面的问题是值类型会崩溃。
我通过添加此方法来修改该方法
因为我喜欢这样做的想法
但我添加了新方法,该方法也将允许采用值类型
最重要的是,有
object
的用法,特别是在存储不同的情况时集合中的类型。或者,当您的组合中可能存在完全任意且不相关的对象时,您需要根据类型使用它们。Generics are not golden hammer. In cases where your activity naturally is non-generic, use good old object. One such case - caching. Cache naturally can hold different types. I've recently seen this implementation of cache wrapper
Question: what for, if
CacheManager
takes object?Then there was real havoc in
Get
The problem above is that value type will crash.
I mended the method by adding this
Because I like idea of doing this
But I added new method, which will allow to take value types as well
The bottom line, there is usage for
object
, especially when storing different types in collection. Or when you have compositions where completely arbitrary and unrelated objects can exist when you need to consume them based on type.其他答案指出了泛型相对于 System.Object 的一般优势。然而,System.Object 优于泛型的一个例子是 Microsoft 如何在 .NET 6+ 中实现新的 ArgumentNullException.ThrowIfNull
https://github.com/dotnet/runtime/issues/82227#issuecomment-1433175719
https://learn.microsoft.com/dotnet/api/system.argumentnullexception.throwifnull
Other answers have pointed out advantages of generics over System.Object in general. However, one example of where System.Object is better than generics - how Microsoft implemented the new ArgumentNullException.ThrowIfNull in .NET 6+
https://github.com/dotnet/runtime/issues/82227#issuecomment-1433175719
https://learn.microsoft.com/dotnet/api/system.argumentnullexception.throwifnull