1

I have this class to generate random objects:

public class Generator<T> { private Random rnd = new Random(); private List<Type> types = new List<Type>(); public Generator() { //can't a generic type constraint that says "implements any interface" so I use this ugly thing... if (!typeof(T).IsInterface) throw new Exception("Generator needs to be instanciated with an interface generic parameter"); types = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.GetInterfaces().Contains(typeof(T))).ToList(); } public T GetRandomObject() { int index = rnd.Next(types.Count); return (T)Activator.CreateInstance(types[index]); } public void SetNewGeneric<N>() { T = N; //this doesn't work... types = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.GetInterfaces().Contains(typeof(T))).ToList(); } } 

I use it like so:

Generator<IEnemy> generator = new Generator<IEnemy>(); var randomEnemy = generator.GetRandomObject(); //create and return randomly, any object that implements IEnemy... 

My question is, is there any way to change the type of T at runtime so I can do something like

generator.SetGeneric<IWeapon>(); var weapon = generator.GetRandomObject(); 

Any other ways I could do this if I'm taking the wrong path ?

Thanks.

2
  • 1
    Then generator is no longer a Generator<IEnemy>. How do you expect that to work, and how do you want to use that? Commented Mar 1, 2016 at 23:43
  • What about making Generator static and having a Set<T> property return a typed generator. So you aren't creating an instance of generator with a given type, but can dynamically set the type and then get random. Commented Mar 1, 2016 at 23:47

3 Answers 3

1

I'd just use a generic method, but not a generic class:

public class Generator { private Random rnd = new Random(); // Make it so you only have to get the interfaces for a given type once. private Dictionary<Type, List<Type>> _typeCache = new Dictionary<Type, List<Type>>(); public T GetRandomObject<T>() { List<Type> types = GetTypes<T>(); int index = rnd.Next(types.Count); return (T)Activator.CreateInstance(types[index]); } private List<Type> GetTypes<T>() { List<Type> types; if (!_typesCache.TryGetValue(typeof(T), out types)) { //can't a generic type constraint that says "implements any interface" so I use this ugly thing... if (!typeof(T).IsInterface) throw new Exception("Generator needs to be instanciated with an interface generic parameter"); types = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.GetInterfaces().Contains(typeof(T))).ToList(); _typesCache[typeof(T)] = types; } return types; } } 

Either that or you'll have to just create a different generator for each type if you want to keep the class generic.

Sign up to request clarification or add additional context in comments.

1 Comment

I marked this one for the solution and the typesCache implementation, to keep the code's performances.
1

You cannot do this...
You don't need a generic class.
Just make your methods generic. Basically just combine all your code in one method

public class Generator { public static T GetRandomObject<T>() { if(typeof(T).IsInterface) throw new Exception("Generator needs to be instanciated with an interface generic parameter"); Random rnd = new Random(); var types = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.GetInterfaces().Contains(typeof(T))).ToList(); int index = rnd.Next(types.Count); return (T)Activator.CreateInstance(types[index]); } } 

Now,if you are worried about performance you could cache the list if types in a
Dictionary< string,List < Type>>

Comments

0

Or you can generate generators:

public class Generator<T> { private readonly Random rnd; private List<Type> types = new List<Type>(); public Generator(Random r) { rnd = r; if (!typeof(T).IsInterface) throw new Exception("Generator needs to be instanciated with an interface generic parameter"); types = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.GetInterfaces().Contains(typeof(T))).ToList(); } public T GetRandomObject() { int index = rnd.Next(types.Count); return (T)Activator.CreateInstance(types[index]); } } public class MetaGenerator { private readonly Random rnd = new Random(); public Generator<T> Create<T>() { return new Generator<T>(rnd); } } var metaGenerator = new MetaGenerator(); var generator = metaGenerator.Create<IEnemy>(); generator = metaGenerator.Create<IWeapon>(); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.