简体   繁体   中英

C# Randomize inherited class without hardcoding

What I want to achieve is to by either name of derived class or enum name generate a random derived class. While my code works in this case, it will require a lot of hardcoding if i decide to expand on it and to me it seems like a bad solution.

Here is my example code: (the UnitTypes variable includes all derived class names dynamically, so i feel like it may be useful - but I could figure out how to.)

public void Run()
        {

            var UnitTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.BaseType == typeof(Unit));
            Unit NewMonster = new Unit(); //<---

        konny: //for testing things out.

            int RandomType = new Random().Next(0, UnitTypes.Count());

            switch (RandomType)
            {
                case 0:
                    NewMonster = new Dingo();
                    break;
                case 1:
                    NewMonster = new Buffalo();
                    break;
                case 2:
                    NewMonster = new Dog();
                    break;
            }
            Console.WriteLine($"A wild {NewMonster.Name} appears");
            NewMonster.Attack();
            Console.ReadLine();

            goto konny;            
        }

And the Baseclass + 1 derived for sake of example.

    public class Unit
    {
        public Enemies Name { get; set; }

        public enum Enemies
        {
            Dingo, Buffalo, Dog
        }

        public virtual void Attack()
        {
            //nothing to see here
        }
   
    }

    class Dingo : Unit
    {

        public Dingo()
        {
            Name = Enemies.Dingo;
        }

        public override void Attack()
        {
            Console.WriteLine($"{Name}  gnaws at your brain");
        }

    }

How about something like this. First create a base class (I have no idea why you named it Unit , I named mine BaseAnimal):

namespace AnimalsTest
{
    public abstract class BaseAnimal
    {    
        public enum EnemiesTypes
        {
            Dingo, Buffalo, Dog
        }

        public abstract IEnumerable<EnemiesTypes> Enemies { get; }

        public abstract void Attack();
    }
}

Note that it's an abstract (non-instantiable) and that any sub-classes need to implement an Enemies property getter and an Attack method.

Then I created three subclasses (in the same namespace):

class Dingo : BaseAnimal
{
    public override IEnumerable<EnemiesTypes> Enemies =>
        new List<EnemiesTypes> { EnemiesTypes.Buffalo, EnemiesTypes.Dog };

    public override void Attack()
    {
        Console.WriteLine($"{nameof(Dingo)} Gnaws at your brain");
    }
}

class Buffalo : BaseAnimal
{
    public override IEnumerable<EnemiesTypes> Enemies =>
        new List<EnemiesTypes> { EnemiesTypes.Dog };

    public override void Attack()
    {
        Console.WriteLine($"{nameof(Buffalo)} Runs you down");
    }
}

class Dog : BaseAnimal
{
    public override IEnumerable<EnemiesTypes> Enemies =>
        new List<EnemiesTypes> { EnemiesTypes.Buffalo };

    public override void Attack()
    {
        Console.WriteLine($"{nameof(Dog)} Barks at you");
    }
}

These are very simple classes, you could make them more complicated.

Finally a test routine that exercises what I think you want. I get the names of the classes from the values of an enum and instantiate them at will.

private static readonly Random Random = new Random();
public static void Test()
{
    var animalClasses = Enum.GetNames(typeof(EnemiesTypes)).Select(n => n.ToString()).ToArray();
    var thisAssembly = Assembly.GetExecutingAssembly().FullName;

    for (var i = 0; i < 6; ++i)
    {
        var className = nameof(AnimalsTest) + "." + animalClasses[Random.Next(0, animalClasses.Length)];
        var animal = (BaseAnimal) Activator.CreateInstance(thisAssembly, className).Unwrap();
        animal.Attack();
        Console.WriteLine($"{animal.GetType().Name} has enemies: {string.Join(", ",animal.Enemies.Select(e=>e.ToString()))}");
        Console.WriteLine();
        ;
    }
}

Note that the test code has no clue about the names of the classes it's working with, it pulls it all from the values of the enum.

Each time I run this, I get a different result (because of, well, Random ). But, here's the output of a typical run:

Buffalo Runs you down
Buffalo has enemies: Dog

Dog Barks at you
Dog has enemies: Buffalo

Buffalo Runs you down
Buffalo has enemies: Dog

Dog Barks at you
Dog has enemies: Buffalo

Dingo Gnaws at your brain
Dingo has enemies: Buffalo, Dog

Dingo Gnaws at your brain
Dingo has enemies: Buffalo, Dog

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM