Подтвердить что ты не робот

С#: System.Object vs Generics

Мне сложно понять, когда использовать Object (boxing/unboxing) против использования дженериков.

Например:

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 у меня могут быть объекты всех типов, которые в настоящее время живут в моем стеке. Так разве это не всегда предпочтительнее? Спасибо!

4b9b3361

Ответ 1

Всегда используйте дженерики! Использование объекта приводит к выполнению операций литья и боксу/распаковке типов значений. Из-за этих причин дженерики быстрее и элегантнее (без кастинга). И - основная причина - вы не получите InvalidCastException с помощью дженериков.

Итак, генераторы быстрее, и ошибки отображаются во время компиляции. System.Object означает исключения во время выполнения и литье, что в целом приводит к снижению производительности (иногда МНОГО ниже).

Ответ 2

Многие люди рекомендуют использовать дженерики, но похоже, что все они не понимают смысла. Это часто не касается производительности, связанной с примитивными типами бокса или кастингом, о том, как заставить компилятор работать для вас.

Если у меня есть список строк, я хочу, чтобы компилятор мне доказывал, что он всегда будет содержать список строк. Generics делает именно это - я указываю намерение, и компилятор доказывает это для меня.

В идеале я бы предпочел еще более богатую систему типов, где вы могли бы сказать, например, что тип (даже если это был ссылочный тип) не мог содержать нулевые значения, но С#, к сожалению, в настоящее время не предлагает этого.

Ответ 3

Вы будете почти всегда использовать дженерики.

Используется приложение System.Object как "общий тип":

  • в старых приложениях pre-FX2, поскольку дженерики недоступны.
  • когда вам нужно смешивать разные типы, как в объектах ASP.NET Session и Application.

Обратите внимание, что в первом примере (не общее) использование выглядит следующим образом:

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

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

Вы действительно хотели бы избежать всего этого приведения типов (для удобства чтения, безопасности и производительности).

Ответ 4

Хотя бывают случаи, когда вы захотите использовать не-общую коллекцию (например, кэширование), у вас почти всегда есть коллекции однородных объектов, а не гетерогенных объектов. Для однородной коллекции, даже если она представляет собой набор вариантов базового типа или интерфейса, всегда лучше использовать дженерики. Это избавит вас от необходимости использовать результат в качестве реального типа, прежде чем вы сможете его использовать. Использование дженериков делает ваш код более эффективным и читабельным, потому что вы можете опустить код для выполнения трансляции.

Ответ 5

С типом object, как вы говорите, вам нужно выполнить бокс и распаковку, которая очень утомительна. С дженериками нет необходимости в этом.

Кроме того, я предпочел бы более конкретно относиться к тому, с какими объектами может работать класс, а generics - отличная основа для этого. Зачем смешивать несвязанные типы данных в первую очередь? В вашем конкретном примере стека подчеркивается преимущество генериков по базовому типу данных object.

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

Помните, что в generics вы можете указать интерфейсы, чтобы ваш класс мог взаимодействовать с объектами многих разных классов при условии, что все они реализуют один и тот же интерфейс.

Ответ 6

При возможности всегда предпочтительны общие характеристики.

Помимо производительности, Generics позволяет вам делать гарантии относительно типов объектов, с которыми вы работаете.

Основная причина, по которой это предпочтение отдается, заключается в том, что компилятор знает, какой тип объекта является объектом, и поэтому он может дать вам ошибки компиляции, которые вы обнаружите сразу, а не ошибки времени выполнения, которые могут произойти только в определенных сценариях, t.

Ответ 7

Все зависит от того, что вам нужно в долгосрочной перспективе.

В отличие от большинства ответов здесь я не буду говорить "всегда использовать дженерики", потому что иногда вам нужно смешивать кошек с огурцами.

Во что бы то ни стало, попробуйте использовать дженерики по всем причинам, уже приведенным в других ответах, например, если вам нужно комбинировать кошек и собак, создать базовый класс Mammal и иметь Stack<Mamal>.

Но когда вам действительно нужно поддерживать все возможные типы, не бойтесь использовать объекты, они не кусаются, если вы не обращаетесь с ними плохо.:)

Ответ 8

Используйте generics, когда вы хотите, чтобы ваша структура обрабатывала один тип. Например, если вам нужен набор строк, вы хотите создать экземпляр строго типизированного списка строк:

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

Если вы хотите, чтобы он обрабатывал несколько типов, вы можете обойтись без генериков, но вы понесете небольшой удар производительности для операций бокса/распаковки.

Ответ 9

Дженерики не золотой молоток. В тех случаях, когда ваша деятельность не является общей, используйте старый добрый объект. Один из таких случаев - кеширование. Кеш, естественно, может содержать разные типы. Я недавно видел эту реализацию обертки кеша

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, особенно при хранении разных типов в коллекции. Или когда у вас есть композиции, в которых могут существовать совершенно произвольные и не связанные объекты, когда вам нужно использовать их в зависимости от типа.

Ответ 10

Иногда вы просто не можете использовать дженерики для этой цели. Например:

public ConstructorName(object defaultObject = null) {}

Если вы измените параметр по умолчанию на generics, у него будет ошибка компиляции.