Как создать общий класс из строки в С#?

У меня есть общий класс:

public class Repository<T> {...}

И мне нужно указать, что с помощью строки... Пример:

string _sample = "TypeRepository";
var _rep = new Repository<sample>();

Как я могу это сделать? Возможно ли это?

Спасибо!

Ответ 1

Вот мои 2 цента:

Type genericType = typeof(Repository<>);
Type[] typeArgs = { Type.GetType("TypeRepository") };
Type repositoryType = genericType.MakeGenericType(typeArgs);

object repository = Activator.CreateInstance(repositoryType);

Отвечая на вопрос в комментарии.

MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething");
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something));
closedMethod.Invoke(repository, new[] { "Query String" });

Ответ 2

Сначала получите объект Type с помощью Type.GetType(stringContainingTheGenericTypeArgument)

Затем используйте typeof(Repository<>).MakeGenericType(theTypeObject), чтобы получить общий тип.

И, наконец, используйте Activator.CreateInstance

Ответ 3

Это задание для ключевого слова dynamic, входящего в С# 4.0.

Здесь есть несколько хороших хаков, которые предоставят вам экземпляр вашего объекта, но независимо от того, какая текущая версия С# будет знать только, что у него есть object, и он не может многое сделать с самим объектом потому что текущий С# не поддерживает позднюю привязку. Вы должны быть в состоянии, по крайней мере, отдать его известному типу, чтобы что-либо сделать с ним. В конечном счете, вы должны знать тип, который вам нужен во время компиляции, или вам не повезло.

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

Ответ 4

Если я правильно понял ваш вопрос... Что вы пытаетесь сделать, это взять ваш тип (Repository <T> ) и построить конкретную общую реализацию этого во время выполнения?

Если да, взгляните на MakeGenericType. Вы можете использовать typeof (репозиторий) и System.Type объекта, который вы хотите для T, и построить его таким образом. Если у вас есть тип, Activator.CreateInstance будет работать для вас.

Ответ 5

Предполагая, что ваша строка содержит имя типа, вы можете написать

object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample)));

Однако _rep будет нетипизированным объектом, и вы не сможете ничего с ним поделать. Поскольку вы не знаете, какой тип универсального класса находится во время компиляции, нет такого типа, к которому вы его применили. (Если репозиторий не наследует не-общий класс или реализует не общий интерфейс)

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

Однако, в зависимости от вашей ситуации, вы, вероятно, захотите сделать Repository не общий класс.

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

Если репозиторий является категоризированным набором вещей, вероятно, лучше всего сделать его не общим, а сделать его конструктор - string category.

Если вы хотите получить более конкретные рекомендации, напишите более подробную информацию о вашей ситуации.

Ответ 6

Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String"));
object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName);

Это создаст экземпляр Repository<string>. Вы можете заменить "System.String" любым типом, который вам нравится.

Ответ 7

Это "возможно". Сорт в том, что вы можете это сделать, но это немного взломать.

У вас есть 3 варианта:

Если вы знаете свои классы спереди, используйте оператор switch. В основном так:

switch(str){
   case "TypeRepository": return new Repository<TypeRepository>;
}

В качестве более продвинутой формы выше вы можете использовать словарь вместо хеш-таблицы

var factory = new Dictionary<string, Func<object>>();
factory.Add( "TypeRepository", () => new Repository<TypeRepository>() );

var theObject = factory["TypeRepository"]() as Repository<TypeRepository>;

Для максимальной гибкости вы можете использовать отражение для соответствия строк классам во время выполнения. Имейте в виду, что отражение довольно медленно, поэтому, если вы делаете это с любой регулярностью, вы хотите избежать этого. Например, здесь используется метод Франса Бумы. Просто измените List<> на Repository<> в своем коде:

public static object MakeList( string typeStr )
{
    var nameSpace = "MyTestApplication" + ".";

    var objType = Type.GetType(nameSpace + typeStr); // NAMESPACE IS REQUIRED
    var listType = typeof(List<>).MakeGenericType(objType);
    return Activator.CreateInstance(listType);
}

var listOfThings = MakeList("MyCoolObject") as List<MyCoolObject>;

Примечание. Невозможно избежать того факта, что все эти механизмы возвращают object, которые затем вам нужно применить к вашему правильному типу, а не возвращать только возвращаемые строго типизированные значения.

Это неизбежно, потому что вы не знаете тип списка до времени исполнения, а С# строится вокруг знания вещей во время компиляции (это то, что люди имеют в виду, когда говорят "статически типизированный язык программирования" ). Это будет менее болезненно в С# 4, где вы сможете вернуть dynamic, что спасет вас от кастинга.

Ответ 8

Работает дженерик по типам, а не по экземплярам. Вы можете прочитать здесь.

Похоже, вам просто нужно добавить конструктор в свой класс, который принимает желаемый тип и инициализирует значение:

public class Foo<T>
{
    private T = default(T);

    public Foo(T initValue)
    {
        _val = T;
    }
}

Ответ 9

"T" в классе Generic обозначает тип, а не экземпляр типа. Поэтому, если вы хотите, чтобы ваш репозиторий сохранял строковые объекты, используйте

var _rep = new Repository<string>();