2016-03-12 12 views
7

Jeśli zaimplementuję typ ogólny, który może być instancjonowany przy użyciu wielu typów parametrów, czy powinienem (z powodu JIT wydajności/rozmiaru kodu itp.) Mieć wiele zagnieżdżonych typów nietypowych?Czy należy unikać typów zagnieżdżonych w typach ogólnych?

Przykład:

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

    private struct SomeOtherHelper 
    { 
     .. 
    } 

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

} 

Alternatywą który działa równie dobrze jest mieć nierodzajową rodzajów zewnątrz, ale potem zanieczyszczają nazw. Czy istnieje najlepsza praktyka?

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

internal struct IndexThingForMyGenericType 
{ 
    int row; int col; 
} 

internal struct SomeOtherHelper 
{ 
    ... 
} 
+0

Jak zawsze, nigdy nie optymalizuj, nigdy nie uderzając w wąskie gardła. Po prostu koduj w sposób, który ma sens, w większości nie będzie to problemem.Jeśli chodzi o pytanie, nie wiem, ale można zobaczyć wiele zagnieżdżonych klas prywatnych w BCL, więc nie miałbym nic przeciwko jeździe z punktu widzenia projektu. Drugi, jak mówisz, może być mylący. – nawfal

+0

Ten kod nie jest semantycznie taki sam. W twoim pierwszym przykładzie 'SomeOtherHelper' nie jest pojedynczym typem, ponieważ zależy od ogólnych parametrów. Na przykład 'typeof (MyGenericType .SomeOtherHelper)' nie jest tym samym, co 'typeof (MyGenericType .SomeOtherHelper)'. – Enigmativity

+0

Jeśli Twoim problemem jest posiadanie klas w przestrzeni nazw, ponieważ chcesz, aby były czystsze, dodaj je do pod-przestrzeni nazw, na przykład, jeśli twój obszar nazw to MyNamespace, a następnie dodaj do obszaru MyNamespace.AccesoryNamespace. – Gusman

Odpowiedz

6

W języku C# każdy typ zagnieżdżony typu generycznego jest z natury ogólny. Kompilator sprawi, że typ zagnieżdżony będzie również generyczny (bez naszej wiedzy). Więcej informacji można znaleźć na stronie this article.

Chociaż generics dzieli kod JIT dla typów referencyjnych, jak wyjaśniono w this interview, ma trochę narzut w porównaniu do klas nie ogólnych. Każdy typ wartości otrzymuje swój własny kod JIT.

  • Jeśli typ jest używany tylko w klasie ogólnej - lepiej mieć prywatny typ zagnieżdżony.

  • Jeśli typ jest używany w innym miejscu, najlepiej powinien być typem niezagnieżdżonym (jako wewnętrzny).

Powiedział, że jeśli zagnieżdżone typy nie używa typ parametru T w tym przypadku, to nie musi być zagnieżdżony typ typu rodzajowego i tym samym staje się typ rodzajowy, jak również.

W większości przypadków nie powinno to mieć znaczenia, ale jeśli obawiasz się wielu typów utworzonych w środowisku wykonawczym, możesz zmienić typ ogólny tak, aby miał nie ogólną klasę podstawową, która działa jak typ kontenera dla typów zagnieżdżonych i wystawiać typy zagnieżdżone jako chronione.

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>> { } 

} 

W ten sposób udostępniasz te same typy zagnieżdżone. Bez obciążania środowiska wykonawczego. Brak oddzielnych typów dla każdego parametru typu. Teraz typeof(MyGenericType<int, string>.SomeOtherHelper) będzie równy typeof(MyGenericType<long, bool>.SomeOtherHelper).

+0

Myślę, że nietypowa klasa podstawowa może być drogą do zrobienia. Jedyną wadą jest to, że w rzeczywistości nie jest użyteczne, ale musi być (przynajmniej tak samo) publiczne jak mój rodzajowy, więc teraz zanieczyszczałem publiczne api, a nie tylko wewnętrzne :( Nadal uważam, że to jest Najgorsze jednak rozwiązanie –

1

Chociaż nie w pełni odpowiada na pytanie, należy pamiętać, że dla typów odniesienia kod jest udostępniany, ponieważ wewnętrznie chodzi tylko o wskaźniki. Więc nie musisz się martwić o nadmiar bazy kodu. Cytuj z odpowiedzi this (Anders Hejlsberg jest cytatem).

Teraz, co wtedy zrobić, to dla wszystkich typów, które dawałaby wartość typy, takie jak List<int>, List<long>, List<double>, List<float> - tworzymy unikalną kopię wykonywalnego kodu natywnego. Tak więc List<int> otrzymuje własny kod. List<long> otrzymuje swój własny kod. List<float> otrzymuje własny kod . Dla wszystkich typów referencyjnych udostępniamy kod, ponieważ są one reprezentatywne identycznie jak . To tylko wskazówki.

+0

Tak, ale nie jest nawet udostępniony dla typów wartości tego samego rozmiaru, więc 'MyGenericType .IndexThing' afaik. nie użyje ponownie 'MyGenericType .IndexThing' itd. Z parametrami typu 2+ obawiam się kombinatorycznej eksplozji, mimo że wszystkie referencje dzielą się kodem! –

+0

@AndersForsgren Tak, wiem. Dlatego powiedziałem, że jest udostępniany dla * typów * referencji. – Kapol

Powiązane problemy