C #: System.Object vs Generics - PullRequest
       0

C #: System.Object vs Generics

26 голосов
/ 13 декабря 2010

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

Например:

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

Ответы [ 10 ]

27 голосов
/ 13 декабря 2010

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

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

8 голосов
/ 13 декабря 2010

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

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

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

7 голосов
/ 13 декабря 2010

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

Используется приложение System.Object в качестве «общего типа»:

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

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

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

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

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

6 голосов
/ 13 декабря 2010

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

2 голосов
/ 13 декабря 2010

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

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

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

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

2 голосов
/ 13 декабря 2010

Дженерики всегда предпочтительнее, если это возможно.

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

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

2 голосов
/ 13 декабря 2010

С типом 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 голос
/ 13 декабря 2010

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

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

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

0 голосов
/ 18 мая 2019

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

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

0 голосов
/ 01 апреля 2019

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

public ConstructorName(object defaultObject = null) {}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...