【问题标题】:How can I create a list of classes in C# to iterate over in a loop如何在 C# 中创建类列表以在循环中进行迭代
【发布时间】:2011-02-16 14:25:42
【问题描述】:

假设我有 animal 类和 catdog 类扩展它。我想做一些类似的事情:

foreach (class a in {cat, dog})  
    if (a.isValid(parameters))  
      doStuff();

isValid 是来自 animal 的静态方法,它只检查给定参数是否定义了给定类型的对象
doStuff 表示我正在做一些我认为不值得复制的事情
@你在说哪些对象?希望我的其他编辑可以澄清这一点
我不是从一个对象开始并试图确定它的类型。我从参数开始,并尝试确定要实例化的动物类型。 所以像 BlueRaja 建议的那样,但不需要构造函数。

我唯一的选择是每个类的 switch 语句,这是我想避免的。

谢谢,
J-B

【问题讨论】:

  • 你能更清楚你的对象来自哪里吗?
  • 问题不是很清楚你想要完成什么。什么是有效?为什么 doStuff 不带任何参数?
  • 您的问题令人困惑。目前尚不清楚您是在迭代 TYPES cat 和 dog 还是这些类型的 INSTANCES。 IsValid 是什么意思完全不清楚,因为“静态”和“虚拟”是对立的;没有“静态虚拟”方法。您能否在问题中提供更多详细信息?
  • @Jean:不同子类上的各个静态方法彼此之间没有有用的关系。如果这确实是您想要做的,那么它会完全改变问题,并且如果没有反射或其他黑客是不可能做到的。
  • @BlueRaja:继承静态方法。静态构造函数不被继承,但静态方法被继承。

标签: c# oop class inheritance list


【解决方案1】:

我认为您正在寻找 typeof 运算符:

Type[] types = new[] { typeof(Cat}, typeof(Dog) };
foreach (Type animalType in types)
{
    // ...
}

虽然你有什么里面循环对此并没有真正的意义;您不能将 Type 转换为 Animal 并且有点难以理解 isValid 方法应该做什么。

听起来您只是想要一个 virtual 方法,但可能需要澄清一些。

编辑:

我不是从一个对象开始并试图确定它的类型。我从参数开始,并试图确定要实例化的动物类型。

那是工厂方法,那么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();
        }
    }
}

您可以使用诸如 Autofac 之类的 DI 库来代替,这将允许您简单地注册甚至扫描类型并通过像这样的工厂方法实例化它们,但在内部它做的事情与上面的代码大致相同.

【讨论】:

  • 我不是从一个对象开始并试图确定它的类型。我从参数开始,并试图确定要实例化的动物类型。
【解决方案2】:

您可以通过调用方法Assembly.GetTypes() 来使用反射枚举程序集中的所有类。

您可以通过调用AppDomain.CurrentDomain.GetAssemblies 获取已加载程序集的列表,也可以通过调用静态方法Assembly.LoadAssembly.LoadFrom 从磁盘加载程序集

【讨论】:

    【解决方案3】:

    在动物基类中将 IsValid 和 doStuff 定义为虚方法,并在 cat 和 dog 类中重写它们以对类执行适当的行为。那么你的循环中就不需要 switch 语句了。

    【讨论】:

    • 我不是从一个对象开始,我是从参数开始并检查它们定义了哪些对象
    【解决方案4】:

    查看 Cast 扩展方法。它允许您强制转换任何 IEnumerable(包括列表和数组)以在 foreach 循环中使用:

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

    【讨论】:

      【解决方案5】:

      让你的动物类实现一个接口,例如IAnimal,然后只需创建一个通用列表以将所有不同类型的动物保存在一个列表中。

      然后,您将在基类(甚至抽象)中使您的 IsValid 方法为虚拟方法,并根据动物类型覆盖它。然后,您可以将其作为方法公开在您的界面上。

      【讨论】:

        【解决方案6】:
          Assembly a = Assembly.GetExecutingAssembly();
          var types = a.GetTypes().Where(t => t.IsSubclassOf(typeof(Animal)));
        

        【讨论】:

          【解决方案7】:

          我不知道我是否超出了范围,但也许您正在寻找类型开关。这个blog post给出了很好的解释和实现,虽然你可以在google上找到更多的替代实现。

          请注意 dthorpe 的解决方案是完全有效的,并且看起来更适合您似乎实现的目标

          【讨论】:

            【解决方案8】:

            不知道为什么每个人都把事情搞得这么复杂

            foreach (Animal a in new Animal[] {new Cat(), new Dog()})  
                if (a.isValid(parameters))  
                    doStuff();
            

            【讨论】:

            • 好吧,除非 Cat/Dog 在内部设置了参数,否则我看不出这样做的目的......
            【解决方案9】:
                Assembly assembly = Assembly.GetExecutingAssembly();
                Type animal = typeof(Animal);
            
                foreach (Type t in assembly.GetTypes())
                {
                    if (animal.IsAssignableFrom(t))
                    {
                        //dog or cat hit.
                    }
                }
            

            这应该适合你。

            【讨论】:

              【解决方案10】:

              我不是从一个对象开始并试图确定它的类型。我从参数开始,并尝试确定要实例化的动物类型。

              真的吗?您的问题确实没有传达这一点。

              无论如何,让我们试一试吧。

              给定一个字符串参数,我们可能想要创建CatDog。让我们编写一个函数来实现这一点:

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

              这看起来一切都很好,但是如果我们在编译时不知道CatDog 是什么,会发生什么?好吧,在某些时候,我们必须有一个将参数映射到实际类型的字典。我们可以在这个字典中查找类型,然后创建它的一个实例。

              代码看起来有点像这样:

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

              这可行,但有两个问题。

              1. Activator.CreateInstance 不是世界上最快的东西。一般来说没问题,但如果你创造了 100,000 只动物,你会注意到
              2. 更重要的是,因为我们只是存储类型并对Animal 进行强制转换,编译器无法为我们检查任何内容。我们可以将typeof(Ferrari) 放入字典中,这样就可以正常编译,但是在运行应用程序时会崩溃。

              下一步是使用泛型。我们可以存储一个 lambda 函数来完成创建动物的工作,而不是将 Type 存储在字典中。这样,我们可以使用泛型,编译器可以全面检查所有动物是否合法。

              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
              }
              

              希望这会有所帮助!

              【讨论】:

                猜你喜欢
                • 2019-03-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-05-29
                • 2014-11-17
                • 2020-09-02
                • 2018-10-14
                • 1970-01-01
                相关资源
                最近更新 更多