2015-01-23 16 views
5

Podczas opracowywania jednego z moich projektów napotkałem problem dotyczący typów ogólnych.Inicjowanie tablicy ogólnych kolekcji o różnych argumentach ogólnych

Projekt wymaga napisania klasy, która działałaby jako źródło obiektów listy. Przypuśćmy, miałem następujące klasy:

public class TablesProvider 
{ 
    private readonly List[] _tables; 

    public TablesProvider() 
    { 
     // initialize the tables var here.... 
    } 

    public List<TItem> GetTable<TItem>() 
    { 
     return (List<TItem>)_tables.Single(x => x is List<TItem>); 
    } 
} 

Klasa ta oczywiście nie działa, ponieważ typ List to typ rodzajowy i dlatego generyczne argumenty powinny zostać określone.

Stworzyłem więc abstrakcyjny typ o nazwie MyList, który zostałby wyprowadzony przez bardziej szczegółowy typ MyList<TItem> w celu uniknięcia tego wymagania i nieco zmodyfikowałam TablesProvider.

Działa to całkiem dobrze. Pozostał tylko jeden problem. Załóżmy, że mam 45 różnych kolekcji, z których każda jest zdefiniowana z innym ogólnym argumentem. Jaki byłby najlepszy sposób na zainicjowanie wszystkich tych kolekcji? Nie mogę korzystać z pętli for tutaj, ponieważ parametry generyczne są określone w czasie kompilacji, a nie w czasie wykonywania, a więc budowa takiego nie byłoby możliwe:

for (int i = 0; i < 45; i++) 
     _tables[i] = new MyList<GenericParameters[i]>(); 

Moim ostatecznym celem jest mieć luksus po prostu zrobić coś takiego ...

var table = _tablesProvider.GetTable<SomeClass>(); 
var element = table[3]; 
var propertyValue = element.SomeProperty; 

... bez konieczności oddanych zmienną element w celu uzyskania dostępu do swoich członków specyficzne dla danego typu.

Warto chyba wspomnieć, że ilość różnych obiektów listy jest ustalona na 45. To się nie zmieni. Teoretycznie mógłbym zainicjować linię tablicową po linii lub mieć zamiast niej 45 właściwości lub zmiennych. Obie te opcje brzmią jednak raczej jako tanie rozwiązanie, ale zaakceptuję jedną z nich, jeśli nie ma innej drogi.

Każdy z was ma jakieś pomysły? Czy robię to zupełnie źle? Czy powinienem wziąć pod uwagę inną strukturę?

Z góry dziękuję.

Odpowiedz

1

ta implementacja działa

public class TablesProvider 
{ 
    private readonly List<object> _tables; 

    public TablesProvider() 
    { 
     _tables = new List<object>(); 
    } 

    public IList<TItem> GetTable<TItem>() 
    { 
     var lst = (List<TItem>)_tables.SingleOrDefault(x => x is List<TItem>); 
     if (lst == null) 
     { 
      lst = new List<TItem>(); 
      _tables.Add(lst); 
     } 
     return lst; 
    } 
} 

tworzy listę TItem kiedy jest to konieczne; następnym razem zwraca tę samą listę dla TItem. to leniwe inicjowanie

więc można powołać

var table = _tablesProvider.GetTable<SomeClass>(); 

bez kodu:

for (int i = 0; i < 45; i++) 
    _tables[i] = new MyList<GenericParameters[i]>(); 

nie jest THREADSAFE

1

Tak, to jest możliwe, aby robić to, co opisują, jeśli używasz refleksji.

Przypuśćmy, że hipotetyczny GenericParameters tablica jest tablicą Type s (ponieważ nie można mieć tablicę typu identyfikatorów), można zdefiniować tę funkcję pomocnika:

private MyList MakeList(Type t) 
{ 
    return (MyList)Activator.CreateInstance(typeof(MyList<>).MakeGenericType(t)); 
} 

I, który pozwoli można to zrobić:

public TablesProvider() 
{ 
    var GenericParameters = new[] { typeof(string), typeof(int), typeof(DateTime) }; 

    _tables = new MyList[GenericParameters.Length]; 

    for (int i = 0; i < GenericParameters.Length; i++) 
    { 
     _tables[i] = MakeList(GenericParameters[i]); 
    } 
} 

można nawet użyć LINQ, jeśli chcesz:

public TablesProvider() 
{ 
    var GenericParameters = new[] { typeof(string), typeof(int), typeof(DateTime) }; 

    _tables = GenericParameters.Select(MakeList).ToArray(); 
} 


Poprzednia odpowiedź:

Cóż, prawda jest taka, że ​​masz zamiar mieć listę 45 różnych typów gdzieś, który dość dużo znaczy masz zamiar mieć 45 różnych linii podobny kod. Można więc powiedzieć, że celem jest uczynienie tych wierszy tak zwięzłymi, jak to możliwe.

Jednym ze sposobów, aby to zrobić byłoby dodać funkcję pomocniczą:

private void AddTable<T>() 
{ 
    _tables.Add(new MyTable<T>()); 
} 

(zakładając zmianę _tables do List<MyTable>)

Następnie można po prostu zrobić:

AddTable<Type1>(); 
AddTable<Type2>(); 
AddTable<Type3>(); 
AddTable<Type4>(); 
+0

cały punkt polega na uniknięciu napisania 45 linii kodu. Nie sądzę, że tego właśnie szuka. – Tweety

+0

@Tweety Być może, ale w jaki sposób wypełniłby swoją hipotetyczną tablicę "GenericParameters []" w pierwszej kolejności bez 45 linii _something_ (czy to kod, plik konfiguracyjny, itp.)? Jego stwierdzony projekt zawiera 45 linii czegoś gdzieś. Chodzi tylko o to, gdzie są i jak wyglądają. – JLRishe