20

I have a Generic class like that :

public class Repository<T> {...}

And I need to instance that with a string ... Example :

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

How can I do that? Is that even possible?

Thanks!

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
Paul
  • 12,359
  • 20
  • 64
  • 101
  • Note that you can't refer to Repository in your program, so the type of _rep will have to be `object` or some interface common to all Repository's. – Anton Tykhyy Mar 26 '09 at 20:18

9 Answers9

27

Here is my 2 cents:

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

object repository = Activator.CreateInstance(repositoryType);

Answering the question in comment.

MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething");
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something));
closedMethod.Invoke(repository, new[] { "Query String" });
alex
  • 74,215
  • 9
  • 49
  • 57
17

First get the Type object using Type.GetType(stringContainingTheGenericTypeArgument)

Then use typeof(Repository<>).MakeGenericType(theTypeObject) to get a generic type.

And finally use Activator.CreateInstance

Andreas Grech
  • 105,982
  • 98
  • 297
  • 360
Frans Bouma
  • 8,259
  • 1
  • 27
  • 28
  • Isn't the real question "should he"? – C. Ross Mar 26 '09 at 20:21
  • from string to genertic type? Perhaps. However MakeGenericType is often used in linq providers, and other expression tree consuming code for example. it's a great helper, although for every day LOB apps it's not something to worry about of course. – Frans Bouma Mar 27 '09 at 09:25
3

This is a job for the dynamic keyword coming in C# 4.0.

There are some nice hacks here that will get you an instance of your object, but no matter what the current version of C# will only know it has an object, and it can't do much with an object by itself because current C# does not support late binding. You need to be able to at least cast it to a known type to do anything with it. Ultimately, you must know the type you need at compile time or you're out of luck.

Now, if you can constrain your repository class to types implementing some known interface, you're in a much better shape.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
2

If I understand your question correctly... What you are trying to do is take your type (Repository<T>) and construct a specific, generic implementation of that at runtime?

If so, take a look at MakeGenericType. You can use typeof(Repository) and the System.Type of the object you want for T and construct it this way. Once you have the type, Activator.CreateInstance will work for you.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

Assuming that your string holds the name of a type, you can write

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

However, _rep will be an untyped object, and you will have no way to do anything with it. Since you don't know what type the generic class is at compile time, there is no type that you cast it to. (Unless Repository inherits a non-generic class or implements a non-generic interface)

You could solve that by making a non-generic base class for Repository and casting the object to it.

However, depending on your situation, you probably want to make Repository a non-generic class.

If Repository is a class that contains other classes, it's probably best to make it non-generic. You could make its constructor take a Type object, and then call Type.IsInstanceOfType on every object you add to make sure it's the right type.

If Repository is a categorized collection of things, it's probably best to make it non-generic, and make its constructor take a string category.

If you want more specific advice, please post more details about you situation.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • My Repository: public class Repository : RepositoryWithTypedId, IRepository { } public class RepositoryWithTypedId : IRepositoryWithTypedId And I can´t change that implementantion... So Is it impossible? – Paul Mar 26 '09 at 20:41
  • It's possible, using the line of code I wrote above, but it would be very difficult to do anything with. Why are you trying to do this, and what are you doing with `_rep`? – SLaks Mar 26 '09 at 20:51
  • My _rep is a DAO (Data Access Object)... – Paul Mar 26 '09 at 20:57
  • But why don't you know the type name at compile time? You might want to make the entire class or method containing _rep generic – SLaks Mar 26 '09 at 21:01
  • I´m using a component, where I can set the NAME of the repository class for each element(of my component) . It´s a treeview with radiobutton... So I get the selected radio (and CLASS name) and I have to get repository... – Paul Mar 26 '09 at 21:08
  • The best way would be to use IoC container to instantiate repositories by whatever display names (e.g. class names, though you can use something else). – alex Mar 26 '09 at 21:13
  • Then the treeview binds to the properties of the Repository? If so, the line of code I gave earlier should work. It will give you an object, which you should be able to give to the treeview (or to anything else). – SLaks Mar 26 '09 at 21:14
  • The treeview binds only the NAME OF THE CLASS to instanciate the correct Repository... It keeps the string... – Paul Mar 26 '09 at 21:19
  • I don't understand what you want to do with the repository after you create it. – SLaks Mar 26 '09 at 21:22
  • The user select RADIO "city", that have class name : "City" ... So, I need instanciate Repository , and get all City from DB to show up... – Paul Mar 27 '09 at 12:47
  • You can instantiate Repository using the line of code I wrote in the answer. I don't know what you're trying to get the City objects to show up in (a grid?), but you should be able to do it by giving the grid (?) the object from my line of code – SLaks Mar 27 '09 at 14:14
  • The repository has all functions to access DB (Get, Save, Del...)... With your line I get a repository, but I can´t use the functions... If you look the first thread´s answer (Alex), there is the "other" part of solution... Thanks anyway! – Paul Mar 27 '09 at 14:21
1
Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String"));
object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName);

This would create an instance of Repository<string>. You can replace "System.String" with whatever type you like.

Matt Brindley
  • 9,739
  • 7
  • 47
  • 51
0

This is "sort of" possible. Sort of in that you can do it, but it's a bit of a hack.

You have 3 options:

If you know your classes up front, use a switch statement. Basically like this:

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

As a more advanced form of the above, you can use a dictionary instead of a hashtable

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

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

For the greatest flexibility, You can use reflection to match strings to classes at runtime. Be aware that reflection is fairly slow, so if you're doing this with any kind of regularity, you want to avoid it. As an example, Here's one using Frans Bouma's method. Just change List<> to Repository<> in your code:

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

Note: There's no way to avoid the fact that all these mechanisms return object, which you then have to cast to your proper type, rather than returning just returning strongly typed values.

This is unavoidable, because you don't know the type of the list until runtime, and C# is built around knowing things at compile time (this is what people mean when they say "statically typed programming language"). It will be less painful in C#4, where you'll be able to return dynamic, which will save you the casting.

Community
  • 1
  • 1
Orion Edwards
  • 121,657
  • 64
  • 239
  • 328
0

Generics work on types, not on instances. You can read more here.

Sounds like you just need to add a constructor to your class that takes in the desired type and initailizes the value:

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

    public Foo(T initValue)
    {
        _val = T;
    }
}
Daniel
  • 1,783
  • 1
  • 14
  • 15
-3

The "T" in a Generic class stands for a type, not an instance of a type. So if you want your repository to hold string objects, then use

var _rep = new Repository<string>();
David
  • 34,223
  • 3
  • 62
  • 80