简体   繁体   English

如何在C#中创建要在循环中迭代的类的列表

[英]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. 假设我有类animal和类catdog扩展它。 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 isValid是来自动物的静态方法,它仅检查给定参数是否定义了给定类型的对象
doStuff means I'm doing stuff that I didn't feel was worth coppying over doStuff表示我正在做一些我认为不值得解决的事情
@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. 因此,建议使用类似BlueRaja的方法,但不需要构造函数。

My only alternative is a switch statement for each class, something I'd like to avoid. 我唯一的选择是每个类的switch语句,我想避免这种情况。

Thanks, 谢谢,
JB JB

Not 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: 我认为您正在寻找typeof运算子:

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. 您无法将Type强制转换为Animal ,这很难理解isValid方法应该执行的操作。

It almost sounds like you just want a virtual method, but some clarification might be in order. 听起来好像您只想要一个virtual方法,但可能需要进行一些澄清。

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: 那就是Factory Method ,而switch语句确实是实现它的最佳方法:

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. 您可以改用诸如Autofac之类的DI库,这将使您可以简单地注册甚至扫描类型,并通过像这样的工厂方法实例化它们,但是在内部它的作用与上面的代码相同。

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. 在动物基类中将IsValid和doStuff定义为虚拟方法,并在cat和dog类中覆盖它们,以对该类执行适当的行为。 Then you won't need a switch statement in your loop. 这样一来,您就无需在循环中使用switch语句。

Look at the Cast extension method. 查看Cast扩展方法。 It allows you to cast any IEnumerable (including lists and arrays) for use in a foreach loop: 它允许您强制转换任何IEnumerable(包括列表和数组)以供foreach循环使用:

foreach (animal a in GetMyCatandDogList()
                    .Cast<animal>()
                    .Where(a => a.IsValid(parameters))
{
    DoStuff(a);
}

Make your animal classes implement an interface eg IAnimal, then just create a generic list to hold all different types of animals in the one list. 让您的动物类实现一个接口,例如IAnimal,然后创建一个通用列表以将所有不同类型的动物保存在一个列表中。

You would then make your IsValid method virtual in the base class (or even abstract) and override it per animal type. 然后,您将使IsValid方法在基类(甚至是抽象类)中虚拟化,并针对每种动物类型覆盖它。 You can then expose this as a method on your interface. 然后,您可以在界面上将此方法公开。

  Assembly a = Assembly.GetExecutingAssembly();
  var types = a.GetTypes().Where(t => t.IsSubclassOf(typeof(Animal)));
    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() . 您可以通过调用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 您可以通过调用AppDomain.CurrentDomain.GetAssemblies来获取已加载程序集的列表,也可以通过调用静态方法Assembly.LoadAssembly.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. 尽管您可以在Google上找到更多替代实现,但该博客文章提供了很好的解释和实现。

Note the dthorpe's solution is perfectly valid and looks like a better fit to what you seem to achieve 请注意,dthorpe的解决方案是完全有效的,并且看起来更适合您似乎要实现的目标

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 . 给定一个字符串参数,我们可能要创建CatDog 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? 这似乎一切都很好,但如果我们不知道会发生什么CatDog是在编译时? 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. 这将起作用,但是有两个问题。

  1. Activator.CreateInstance is not the fastest thing in the world. Activator.CreateInstance不是世界上最快的东西。 Generally it's fine but if you're creating 100,000 animals you'll notice 通常情况很好,但是如果您要创建100,000只动物,您会注意到
  2. More importantly, because we are just storing types and doing casts to Animal , the compiler can't check anything for us. 更重要的是,由于我们只是存储类型并对Animal进行强制类型转换,因此编译器无法为我们检查任何内容。 We could put typeof(Ferrari) in the dictionary and it would all compile fine, but it would crash when the app was run. 我们可以将typeof(Ferrari)放入字典中,并且可以正常编译,但是在运行应用程序时会崩溃。

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. 除了将Type存储在字典中之外,我们还可以存储一个lambda函数来完成创建动物的工作。 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! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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