简体   繁体   English

C#:System.Object 与 Generics

[英]C#: System.Object vs Generics

I'm having a hard time understanding when to use Object (boxing/unboxing) vs when to use generics.我很难理解何时使用 Object(装箱/拆箱)与何时使用 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. 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.似乎使用 System.Object 方式,我可以拥有当前存在于我的堆栈中的各种类型的对象。 So wouldn't this be always preferable?那么这不是总是更可取吗? Thanks!谢谢!

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. 而且-主要原因-您不会使用泛型获得InvalidCastException

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). System.Object表示运行时异常和强制转换,通常会导致性能降低(有时会降低很多)。

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. 理想情况下,我希望使用更丰富的类型系统,例如,您可以说类型(即使它是引用类型)不能包含空值,但不幸的是C#目前不提供空值。

You will almost always want to use generics. 几乎总是想使用泛型。

The application of System.Object as a 'general type' is used: 使用System.Object作为“通用类型”的应用程序:

  • in old, pre-FX2 applications, because generics weren't available. 在旧的FX2之前的应用程序中,因为无法使用泛型。
  • when you need to mix different types, as in the ASP.NET Session and Application objects. 当您需要混合使用不同的类型时,例如在ASP.NET会话和应用程序对象中。

Note that in you first sample (non-generic) the usage looks like: 请注意,在第一个样本(非泛型)中,用法如下所示:

Stack s = ...;
s.Push("Hello");
s.Push(1.23);

double d = (double) s.Pop();
string t = (string) s.Pop();

You would really want to avoid all this typecasting (for readability, safety and performance). 您真的要避免所有这些类型转换(出于可读性,安全性和性能)。

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. 使用泛型可以使您的代码更高效,更易读,因为您可以省略代码进行强制转换。

With the object type, as you say you need to perform boxing and unboxing, which gets tedious very quickly. 正如您所说,使用object类型需要执行装箱和拆箱操作,这很快就会变得很乏味。 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. 您的特定堆栈示例强调了泛型优于基本object数据类型的优点。

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

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. 请记住,使用泛型可以指定接口,以便您的类可以与许多不同类的对象进行交互,前提是它们都实现相同的接口。

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. 首选使用强制转换的主要原因是编译器知道对象的类型,因此它可以为您提供立即发现的编译错误,而不是仅在未测试的某些情况下才可能发生的运行时错误。

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> . 无论如何,出于其他答案中已经给出的所有原因,请尝试坚持使用泛型,例如,如果您需要组合猫和狗,请创建基类Mammal并使用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. 但是,当您确实需要支持所有可能的类型时,请不要害怕使用对象,除非您误用了它们,否则它们不会咬人。 :) :)

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: 例如,如果您想要一个字符串集合,则需要实例化一个强类型的字符串列表,如下所示:

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

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 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 我最近看到了缓存包装器的这种实现

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

Question: what for, if CacheManager takes object? 问题:如果CacheManager带对象,该怎么办?

Then there was real havoc in Get 然后在Get发生了真正的破坏

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

The problem above is that value type will crash. 上面的问题是值类型将崩溃。

I mended the method by adding this 我通过添加此方法来修改方法

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

Because I like idea of doing this 因为我喜欢这样做的想法

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

But I added new method, which will allow to take value types as well 但是我添加了新方法,该方法也可以采用值类型

public object GetCacheItem(string cacheKey)

The bottom line, there is usage for object , especially when storing different types in collection. 最重要的是,有object用法,尤其是在集合中存储不同类型的object时。 Or when you have compositions where completely arbitrary and unrelated objects can exist when you need to consume them based on type. 或者,当您需要根据类型使用它们时,当您拥有可以完全任意和不相关的对象存在的构图时。

Sometimes you just can't use generics for the purpose. 有时您只是不能使用泛型来实现此目的。 For example: 例如:

public ConstructorName(object defaultObject = null) {}

If you change the default parameter to generics, it would have a compile error. 如果将默认参数更改为泛型,则会出现编译错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM