从类中更改泛型类型

时间:2016-03-01 23:38:09

标签: c# .net generics

我有这个类来生成随机对象:

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();
    }

}

我这样使用它:

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

我的问题是,有没有办法在运行时更改T的类型,所以我可以做类似的事情

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

如果我走错路,我还能采取其他任何方式吗?

感谢。

3 个答案:

答案 0 :(得分:1)

你不能这样做...
您不需要通用课程 只需使您的方法通用。 基本上只需将所有代码组合在一个方法中

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]);
    }
}

现在,如果你担心性能,你可以缓存列表中的类型    字典&LT; string,List&lt;类型&GT;&GT;

答案 1 :(得分:1)

我只使用通用方法,但不是通用类:

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

如果你想让这个类保持通用,你或者你必须为每种类型创建一个不同的生成器。

答案 2 :(得分:0)

或者您可以生成生成器:

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