2015-01-23 2 views
5

Во время разработки одного из моих проектов я столкнулся с проблемой общих типов.Инициализация массива общих коллекций с каждым другим общим аргументом

Проект требует от меня написать класс, который будет служить источником объектов списка. Предположим, что я имел следующий класс:

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>); 
    } 
} 

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

Так что я сделал абстрактный тип под названием MyList, который будет получен более конкретным типом MyList<TItem> для того, чтобы избежать этого требования, и отредактировал TablesProvider немного.

public class TablesProvider 
{ 
    private readonly MyList[] _tables; 

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

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

public abstract class MyList 
{ 
    // ... 
} 

public class MyList<TItem> : MyList, IList<TItem> 
{ 
    private readonly List<TItem> _elements = new List<TItem>(); 

    public TItem this[int index] 
    { 
     get { return _elements[index]; } 
     set { _elements[index] = value; } 
    } 

    // ... 
} 

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

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

Моя конечная цель состоит в том, чтобы позволить себе роскошь просто сделать что-то вроде этого ...

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

... без необходимости бросить переменную element для того, чтобы получить доступ к своим членам типоспецифические.

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

У любого из вас есть идеи? Я делаю это совершенно неправильно? Должен ли я рассмотреть другую структуру?

Заранее спасибо.

ответ

1

эта реализация работает

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

создает Список TItem в случае необходимости; в следующий раз он вернет тот же список для TItem. это ленивая инициализация

так что вы можете сделать, вызвать

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

без кода, как это:

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

это не THREADSAFE

1

Да, можно делать то, что вы описывают, если вы используете отражение.

Предположив, что ваш гипотетический GenericParameters массив представляет собой массив Type с (так как вы не можете иметь массив типа идентификаторов), вы можете определить эту вспомогательную функцию:

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

И это позволит вы сделать это:

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]); 
    } 
} 

вы можете даже использовать LINQ, если вы хотите:

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

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


Предыдущий ответ:

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

Один из способов сделать это было бы добавить вспомогательную функцию:

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

(это предполагает изменение _tables к List<MyTable>)

Тогда вы можете просто сделать:

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

цель состоит в том, чтобы не писать 45 строк кода. Я не думаю, что это то, что он ищет. – Tweety

+0

@Tweety Возможно, но как бы он заполнил свой гипотетический массив GenericParameters [] 'в первую очередь без 45 строк _something_ (будь то код, файл конфигурации и т. Д.)? Его заявленный дизайн предполагает наличие 45 строк чего-то. Это просто вопрос, где они находятся и как они выглядят. – JLRishe

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