How can I create a list of classes in C# to iterate over in a loop
Suppose I have class animal
and classes cat
and dog
extending it. I want to do something along the lines of:
foreach (class a in {cat, dog})
if (a.isValid(parameters))
doStuff();
isValid is a static method from animal that just checks if the given parameters define an object of the given type
doStuff means I'm doing stuff that I didn't feel was worth coppying over @which objects are you talking about? hopefully my other edits clear this up I'm not starting with an object 开发者_开发百科and trying to determine it's type. I'm starting with parameters and trying to decide which animal type to instantiate as. So something like BlueRaja suggested, but without needing constructors.My only alternative is a switch statement for each class, something I'd like to avoid.
Thanks,
J-BNot sure why everyone made this so complicated
foreach (Animal a in new Animal[] {new Cat(), new Dog()})
if (a.isValid(parameters))
doStuff();
I think you're looking for the typeof
operator:
Type[] types = new[] { typeof(Cat}, typeof(Dog) };
foreach (Type animalType in types)
{
// ...
}
Although what you've got inside the loop doesn't really make sense for this; you can't cast Type
to Animal
and it's a bit hard to understand what the isValid
method is supposed to be doing.
It almost sounds like you just want a virtual
method, but some clarification might be in order.
Edit:
I'm not starting with an object and trying to determine it's type. I'm starting with parameters and trying to decide which animal type to instantiate as.
That's a Factory Method, then, and a switch
statement really is the best way to implement it:
public enum AnimalType
{
Cat,
Dog,
...
}
public static class AnimalFactory
{
public static Animal CreateAnimal(AnimalType type)
{
switch (type)
{
case AnimalType.Cat:
return new Cat();
case AnimalType.Dog:
return new Dog();
default:
throw new NotImplementedException();
}
}
}
You can use a DI library such as Autofac instead, that will allow you to simply register or even scan for the types and instantiate them via a factory method like this one, but internally it's doing much the same thing as the code above.
Assembly a = Assembly.GetExecutingAssembly();
var types = a.GetTypes().Where(t => t.IsSubclassOf(typeof(Animal)));
Define IsValid and doStuff as a virtual methods in the animal base class and override them in the cat and dog classes to perform the appropriate behavior to the class. Then you won't need a switch statement in your loop.
Look at the Cast extension method. It allows you to cast any IEnumerable (including lists and arrays) for use in a foreach loop:
foreach (animal a in GetMyCatandDogList()
.Cast<animal>()
.Where(a => a.IsValid(parameters))
{
DoStuff(a);
}
Make your animal classes implement an interface e.g. IAnimal, then just create a generic list to hold all different types of animals in the one list.
You would then make your IsValid method virtual in the base class (or even abstract) and override it per animal type. You can then expose this as a method on your interface.
Assembly assembly = Assembly.GetExecutingAssembly();
Type animal = typeof(Animal);
foreach (Type t in assembly.GetTypes())
{
if (animal.IsAssignableFrom(t))
{
//dog or cat hit.
}
}
This should work for you.
You can use reflection to enumerate all classes in an assembly by calling method Assembly.GetTypes()
.
You can get a list of loaded assemblies by calling AppDomain.CurrentDomain.GetAssemblies
, or you can load your assemblies from disk by calling static methods Assembly.Load
or Assembly.LoadFrom
I don't know if i'm out of line, but maybe you are searching for a type switch. This blog post gives a good explanation and implementation, although you can find more alternative implementations on google.
Note the dthorpe's solution is perfectly valid and looks like a better fit to what you seem to achieve
I'm not starting with an object and trying to determine it's type. I'm starting with parameters and trying to decide which animal type to instantiate as.
Really? Your question really doesn't convey this.
At any rate, let's give it a go.
Given a string parameter, we may want to create either a Cat
or a Dog
. Let's write a function which does that:
public Animal CreateAnimal(string parameter)
{
if(parameter == "cat")
return new Cat();
else
return new Dog();
}
var strings = new[]{ "cat", "cat", "dog" };
var animals = strings.Select(s => CreateAnimal(s)).ToList();
This seems all well and good, but what happens if we don't know what Cat
and Dog
are at compile time? Well, at some point we have to have a dictionary which maps parameters to actual types. We can look in this dictionary to find the type, then create an instance of it.
The code would look a bit like this:
public static Dictionary<string, Type> lookup = {
{ "cat", typeof(Cat) },
{ "dog", typeof(Dog) },
{ "fish", typeof(Fish) }, // people can add things to lookup dynamically
}
public Animal CreateAnimal(string parameter)
{
var animalType = lookup[parameter];
return (Animal)Activator.CreateInstance(animalType);
}
This will work, but there are 2 problems.
Activator.CreateInstance
is not the fastest thing in the world. Generally it's fine but if you're creating 100,000 animals you'll notice- More importantly, because we are just storing types and doing casts to
Animal
, the compiler can't check anything for us. We could puttypeof(Ferrari)
in the dictionary and it would all compile fine, but it would crash when the app was run.
The next step is to use generics. Instead of storing the Type
in the dictionary, we can store a lambda function which does the work of creating the animal. That way, we can use generics, and the compiler can fully check that all the animals are legit.
public static Dictionary<string, Func<Animal>> lookup = {
{ "cat", () => new Cat() },
{ "dog", () => new Dog() },
{ "fish", () => new Fish() }, // people can add things to lookup dynamically
}
public Animal CreateAnimal(string parameter)
{
var creatorFunc = lookup[parameter];
return creatorFunc(); // call the lambda which does the creating
}
Hope this helps!
精彩评论