2016-03-12 2 views
7

Если я реализую общий тип, который может быть подвергнут экземпляру с большим количеством параметров типа, следует ли избегать (по причинам JIT производительности/размера кода и т. Д.), Имеющих много вложенных не общих типов?Должен ли я избегать вложенных типов в общих типах?

Пример:

public class MyGenericType<TKey, TValue> 
{ 
    private struct IndexThing 
    { 
     int row; int col; 
    } 

    private struct SomeOtherHelper 
    { 
     .. 
    } 

    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 

} 

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

public class MyGenericType<TKey, TValue> 
{ 
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 
} 

internal struct IndexThingForMyGenericType 
{ 
    int row; int col; 
} 

internal struct SomeOtherHelper 
{ 
    ... 
} 
+0

Как всегда никогда не оптимизируйте, не забивая узкие места. Просто код, который имеет наибольший смысл, в основном это не проблема.Что касается вопроса, я не знаю, но вы можете увидеть много вложенных частных классов в BCL, поэтому я бы не пропустил этот маршрут с дизайнерской точки зрения. Второй, как вы говорите, может ввести в заблуждение – nawfal

+0

Этот код не семантически то же самое. В первом примере «SomeOtherHelper» не является одним типом, поскольку он зависит от общих параметров. Например, 'typeof (MyGenericType .SomeOtherHelper)' это не то же самое, что 'typeof (MyGenericType .SomeOtherHelper)'. – Enigmativity

+0

Если ваша проблема состоит в том, чтобы иметь классы в пространстве имен, потому что вы хотите, чтобы они были более чистыми, добавьте их в пространство под-имен, например, если ваше пространство имен - это MyNamespace, а затем добавьте в MyNamespace.AccesoryNamespace. – Gusman

ответ

6

В C# каждый вложенный тип родового типа по своей природе является общим. Компилятор сделает вложенный тип также универсальным (без нашего ведома). Для получения дополнительной информации см. this article.

Хотя дженерики разделяют код JIT для ссылочных типов, как описано в this interview, у него есть некоторые накладные расходы по сравнению с не-универсальными классами. Каждый тип значения получает свой собственный JIT-код.

  • Если тип используется только в общем классе - имеет смысл быть частным вложенным типом.

  • Если тип используется в другом месте, то в идеале он должен быть не вложенным (как внутренний).

Тем не менее, если ваши вложенные типы не используется тип параметра T в этом случае он не должен быть вложенным тип универсального типа и, таким образом, он становится универсальным типом, а также.

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

public class NonGenericBase 
{ 
    protected struct IndexThing 
    { 
     int row; int col; 
    } 

    protected struct SomeOtherHelper 
    { 
     .. 
    } 
} 

public class MyGenericType<TKey, TValue> : NonGenericBase 
{ 
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 

} 

Таким образом, вы используете одни и те же вложенные типы. Накладные расходы не работают. Нет отдельных типов для каждого параметра типа. Теперь typeof(MyGenericType<int, string>.SomeOtherHelper) будет равно typeof(MyGenericType<long, bool>.SomeOtherHelper).

+0

Я думаю, что базовым классом не общий, может быть, путь. Только плохая вещь, если она на самом деле не полезна, она по-прежнему должна быть (по крайней мере, как) общедоступной, как мой общий тип, поэтому теперь я загрязнил публичную api, а не только внутреннюю :( Я все еще думаю, что это хотя и плохое решение. –

1

Хотя это не полностью отвечает на вопрос, обратите внимание, что для ссылочных типов код является общим, потому что внутренне это все касается указателей. Поэтому вам не нужно беспокоиться о раздувании базы кода. Цитата из this Ответ (Андерс Хейлсберг - это цитата).

Теперь, что мы тогда делаем для всех конкретизации типа, которые являются значение типы, такие как List<int>, List<long>, List<double>, List<float> - мы создаем уникальную копию исполняемого машинного кода. Так List<int> получает собственный код. List<long> получает собственный код. List<float> получает свой собственный код . Для всех ссылочных типов мы разделяем код, потому что они репрезентативно идентичны. Это просто указатели.

+0

Да, но он даже не используется для типов значений того же размера, поэтому «MyGenericType .IndexThing' afaik. не будет повторно использовать «MyGenericType .IndexThing» и т. д. С параметрами типа 2+ я боюсь комбинаторного взрыва, даже если все ссылочные экземпляры используют код! –

+0

@ AndersForsgren Да, я знаю. Вот почему я сказал, что он доступен для типов * reference *. – Kapol

Смежные вопросы