views:

1244

answers:

9

Hello ... 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!

A: 

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
I think you've misunderstood the question - the string contains a type name.
Daniel Earwicker
+12  A: 

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

Frans Bouma
Isn't the real question "should he"?
C. Ross
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
A: 

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.

Orion Edwards
Given that the type is being constructed using a string, how would C# reasonably associate a specific type with the result?
Daniel Earwicker
A: 

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
+2  A: 

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
+5  A: 

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
And How can I access the repository methods?
Paul
The same way you have created repository for the type that only known in run-time, using reflection. You want flexibility, you need to pay for it.
alex
Can you show me how to do that?Thanks!
Paul
It worked! Thanks Alex!
Paul
You are very welcome, glad that I was able to help.
alex
A: 
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.

Matthew Brindley
+2  A: 

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
My Repository:public class Repository<T> : RepositoryWithTypedId<T, int>, IRepository<T> { }public class RepositoryWithTypedId<T, IdT> : IRepositoryWithTypedId<T, IdT>And I can´t change that implementantion... So Is it impossible?
Paul
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
My _rep is a DAO (Data Access Object)...
Paul
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
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
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
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
The treeview binds only the NAME OF THE CLASS to instanciate the correct Repository... It keeps the string...
Paul
I don't understand what you want to do with the repository after you create it.
SLaks
The user select RADIO "city", that have class name : "City" ...So, I need instanciate Repository<City> , and get all City from DB to show up...
Paul
You can instantiate Repository<City> 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
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
+2  A: 

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
So, I don´t have a solution to my problem...
Paul
Nope, and my point is that you won't get one that really works before C# 4 comes out unless you can re-think your architecture.
Joel Coehoorn
If you share what you intend to do with these repository objects, we might be able to help you with a design that works better.
Joel Coehoorn