Динамическое объявление экземпляра универсального типа

Можно ли объявить экземпляр универсального шаблона, не зная его тип во время разработки?

Пример:

Int i = 1;
List<typeof(i)> list = new List<typeof(i)>();

где тип i может быть любым, вместо того, чтобы делать:

List<int> list = new List<int();
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
30
0
47 522
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Я думаю, лучшее, что вы сможете сделать, это что-то вроде этого:

static void Main(string[] args)
{
    int i = 1;
    var thelist = CreateList(i);
}

public static List<T> CreateList<T>(T t)
{
    return new List<T>();
}

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

Jon Skeet 21.11.2008 10:10

Совершенно уверен, что вы можете это сделать, их не нужно исправлять во время компиляции, как шаблоны в С ++.

Пример, похожий здесь: http://geekswithblogs.net/marcel/archive/2007/03/24/109722.aspx

Если вы не знаете тип во время разработки, я бы сказал, что у вас есть список ОБЪЕКТОВ (базовый класс для всех других типов).

List<object> list = new List<object>();

Нет, вы все равно можете создать список соответствующего типа и передать его чему-то, что может выполнить приведение к нужному типу и впоследствии работать с ним статически типизированным способом. Тот факт, что один фрагмент кода не знает типа, не означает, что ничего такого знает этот тип.

Jon Skeet 21.11.2008 10:12

Вы также можете использовать Activator.CreateInstance. Пример фрагмента кода:

public class BaseRepository<T> where T : DataContext
{
   protected T _dc;

   public BaseRepository(string connectionString)
   {
      _dc = (T) Activator.CreateInstance(typeof(T), connectionString);
   }

   public void SubmitChanges()
   {
      _dc.SubmitChanges();
   }
}

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

Кстати, вы не можете вызвать typeof для экземпляра - чтобы получить тип экземпляра (например, "i" вызывает GetType ():

Type intType = i.GetType();
Ответ принят как подходящий

Если вы не знаете тип во время компиляции, но хотите фактический тип (т.е. не List<object>) и, вы не используете общий метод / тип с соответствующим параметром типа, тогда вам нужно использовать отражение.

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

object x = GetObjectFromSomewhere();
// I want to create a List<?> containing the existing
// object, but strongly typed to the "right" type depending
// on the type of the value of x
MethodInfo method = GetType().GetMethod("BuildListHelper");
method = method.MakeGenericMethod(new Type[] { x.GetType() });
object list = method.Invoke(this, new object[] { x });

// Later

public IList<T> BuildListHelper<T>(T item)
{
    List<T> list = new List<T>();
    list.Add(item);
    return list;
}

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

Обновлено: обратите внимание, что хотя я вызываю Type.GetMethod в приведенном выше коде, если вы собирались выполнять его много, вы, вероятно, захотели бы просто вызвать его один раз - в конце концов, метод не изменится. Вы можете сделать его статическим (вы могли бы сделать это в приведенном выше случае), и вы, вероятно, тоже захотите сделать его приватным. Я оставил его как метод общедоступного экземпляра для простоты вызова GetMethod в примере кода - в противном случае вам нужно было бы указать соответствующие флаги привязки.

Вы можете вернуть неуниверсальный IList (который поддерживается List <>), тогда возвращенный список будет полезен.

configurator 28.11.2008 21:42

В этом случае было бы проще использовать ArrayList для начала.

Jon Skeet 28.11.2008 23:05

ах, но добавление неправильного типа в коллекцию, когда она приведена как IList, все равно вызовет исключение о том, что это неправильный тип, не так ли?

Sekhat 22.12.2008 15:52

Да, это вызовет исключение.

Jon Skeet 22.12.2008 16:48

@JonSkeet, почему не что-то вроде List <type> list; разрешено в первую очередь? Это потому, что это противоречит парадигме статической типизации?

Kakira 05.06.2014 00:59

@Kakira: Да, цель дженериков - обеспечить безопасность типов во время компиляции. Какой был бы тип list[0]? Какой переменной вы могли бы ее присвоить?

Jon Skeet 05.06.2014 09:44

12 лет спустя, и этот ответ мне снова помогает! Спасибо @JonSkeet

benPearce 23.11.2020 07:56

Если вы все еще хотите ввести .Add (), .Remove (), do foreach и т. д., Вы можете рассматривать List как обычный "старый" System.Collections.IList, поскольку этот интерфейс, к счастью, реализован List <T>.

И поскольку все другие опубликованные ответы на этот вопрос показывают практически все возможные способы динамического создания экземпляра List <T>, Я покажу последний способ сделать это. Я лично использую этот метод при создании универсальных экземпляров, когда я действительно ничего не знаю о типе во время компиляции, и тип должен быть передан в виде строки, возможно, из файла конфигурации приложения. В этом примере T - это System.String для простоты, но это может быть что угодно:

Type T = typeof ( string ); // replace with actual T
string typeName = string.Format (
  "System.Collections.Generic.List`1[[{0}]], mscorlib", T.AssemblyQualifiedName );

IList list = Activator.CreateInstance ( Type.GetType ( typeName ) )
  as IList;

System.Diagnostics.Debug.Assert ( list != null ); //

list.Add ( "string 1" ); // new T
list.Add ( "string 2" ); // new T
foreach ( object item in list )
{
  Console.WriteLine ( "item: {0}", item );
}

Другие вопросы по теме