C#: Question about using generics and picking a random type
In my game I'm using a powerup system, which basically works like this:
- Enemy gets killed, has random chance to drop powerup box
- Pickup powerup box to get random item
The code for giving the player a random powerup looks like this...
Type t = RandomManager.PickFromParams<Type>(typeof(PowerupInvincible), typ开发者_开发技巧eof(PowerupDoubleFireRate));
ply.AddPowerup<t>();
And the AddPowerup<>()
method looks like this:
public void AddPowerup<T>() where T : PowerupEffect, new()
{
var powerup = new T();
powerup.Activate(this);
powerups.Add(powerup);
}
Now, the problem is the ply.AddPowerup<t>()
line, because it complains that it can't find the type t:
The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?)
Does anyone know if there's a way to fix this, or if that's not possible, show me a better way to do it? Thanks in advance!
It sounds like you are not using generics correctly in this instance. I'm assuming PowerupInvincible
, and PowerupDoubleFireRate
inherit from some PowerupBase
:
PowerupBase powerUp = RandomManager.PickFromParams<PowerupBase>(typeof(PowerupInvincible), typeof(PowerupDoubleFireRate));
ply.AddPowerup(powerUp);
Then the method signature simple need to be:
public void AddPowerup(PowerupBase powerup)
{
powerup.Activate(this);
powerups.Add(powerup);
}
Your random manager would be responsible for choosing one of the parameters, instantiating it, and then returning the instance.
Generics are compile type. t
is the type at runtime. You will have to invoke the method using reflection or.
http://blogs.microsoft.co.il/blogs/gilf/archive/2008/10/10/invoking-generic-methods-with-reflection.aspx
you can do something like
public void AddPowerup(Type powerupType) {
if (!powerupType.IsSubclassOf(typeof(PowerupEffect)))
throw some exception here;
var powerup = Activator.Create(powerupType);
....
}
you can fix it - but only with reflection - you have to find the MethodInfo for AddPowerup, make a generic-version from it with type t and then invoke this method for the object ply.
You can't pass Type arguments at runtime, only compile time. So you would need to hard-code the type rather than using t.
You should probably consider using dependency injection, where AddPowerup() does not actually create the new PowerupEffect, but instead takes a PowerupEffect object through a parameter.
The T
in <T>
should be the real name of a type, not a variable of type Type
.
Instantiating generic types is described here:
// Use the typeof operator to create the generic type // definition directly. To specify the generic type definition, // omit the type arguments but retain the comma that separates // them. Type d1 = typeof(Dictionary<,>); // You can also obtain the generic type definition from a // constructed class. In this case, the constructed class // is a dictionary of Example objects, with String keys. Dictionary<string, Example> d2 = new Dictionary<string, Example>(); // Get a Type object that represents the constructed type, // and from that get the generic type definition. The // variables d1 and d4 contain the same type. Type d3 = d2.GetType(); Type d4 = d3.GetGenericTypeDefinition();
The good thing about reflection, is that you can forgo the need to code a list of implementors:
// TODO proper caching
// TODO proper random generation
var randomType = Assembly.GetAssembly(typeof(MainClass))
.GetTypes()
.Where(t => typeof(B).IsAssignableFrom(t))
.Where(t => !(t.IsAbstract || t.IsInterface))
.Except(new [] { typeof(PowerupBase) })
.OrderBy (t => new Random(DateTime.Now.Millisecond).Next())
.First();
Activator.CreateInstance(x);
精彩评论