【问题标题】:enumerate through all IEnumerables枚举所有 IEnumerables
【发布时间】:2009-10-07 13:15:55
【问题描述】:

我需要将不同的 IEnumerables 发送到 Printer 对象。 然后,此打印机对象将在 foreach 循环中对它们执行某些操作。

class Printer
{
    public Printer(IEnumerable list)
    {
        foreach (var enumerable in list)
        {
            //DO STUFF
        }
    }
}

这让我可以向打印机对象发送任何可枚举,例如 List<T>。 比如

    var list = new List<string> {"myList"};
    new Printer(list); //mylist

这很好用。 但是如果我发送Dictionary&lt;T, T&gt;,例如:

var dictionary = new Dictionary<int, string> {{1, "mydict"}};
new Printer(dictionary); //[1, mydict]

它将有一个键和一个值。不过,我想要的是单独访问 foreach 循环内的 Value 属性。我只能访问可枚举的对象,它没有我可以使用的属性。

现在,如果数据类型 T 是一个包含多个属性的对象(这适用于两个示例)。我如何能够在我的 foreach 循环中使用这些属性?

老实说,我是否必须创建一个构造函数的重载,用于我可能发送给它的每个可能的数据类型?

另外,我需要在 foreach 中做的所有事情都不依赖于任何数据类型 - 因为它不会操纵所有内容。不过,我确实需要访问所有属性。

另外,这只是示例代码,实际上并不是我在应用程序中使用的生产代码。

【问题讨论】:

  • 你不会在第一个例子中遇到同样的问题吗?毕竟你可以得到一个 List ,其中 T 是一个包含多个属性的复杂对象?
  • 你确定它接受 IEnumerable 而不是 IEnumerable?
  • 是的,这就是我在“现在该怎么办”段落中笨拙地试图解释的内容。我会尝试编辑我的帖子以使其更清晰。

标签: c# ienumerable


【解决方案1】:

你能改变打印机类的代码吗?如果它接受类似IEnumerable&lt;IPrintable&gt; 而不仅仅是IEnumerable 之类的东西会更容易。使用这样的界面:

interface IPrintable
{
    void Print();
}

然后将发送到Printer 的所有对象都需要实现该接口。然后你可以这样做:

class Printer
{
    public Printer(IEnumerable<IPrintable> list)
    {
        foreach (var enumerable in list)
        {
            enumerable.Print();
        }
    }
}

如果你有一个可打印对象的字典,比如:

var dict = new Dictionary<int,IPrintable>();

您可以将值传递给函数:

var printer = new Printer(dict.Values);

【讨论】:

  • 这似乎是一个聪明的解决方案!我会让线程存活几个小时。如果没有人胜过你的解决方案,我会接受你的回答
【解决方案2】:

您可以修改您的方法以接受返回打印方法所需数据的委托。像这样的:

// You will not need this class, if you always want a single string result.
class PrinterData
{
    public string Value { get; set; }
    // More properties?
}
class Printer
{
    public Printer<T>(IEnumerable<T> list, Func<T, PrinterData> func)
    {
        foreach (T item in list)
        {
            PrinterData data = func(item);
            // Do something with the data.
        }
    }
}

用法:

int[] ints = new int[] {1,2,3};
new Printer().Print(ints, x => new PrinterData() { Value = x.ToString() });

var dictionary = new Dictionary<int, string> {{1, "mydict"}};
new Printer().Print(dictionary, x => new PrinterData() { Value = x.Name + " = " + x.Value });

Erik Stendahl 的回答非常相似。

【讨论】:

    【解决方案3】:

    在调用 new Printer() 之前,您必须提取一个带有您想要传递的值的枚举。对于字典,这很简单:只需使用 dict.Values。更一般的情况是:

    var list = List<MyObject>()...
    
    var printer = new Printer(list.Select(x => x.MyProperty));
    

    【讨论】:

      【解决方案4】:

      如果你想以不同的方式对待不同的类型,你可能应该使用不同的方法。如果你想对它们一视同仁,你应该接受一个通用接口,并且只使用为接口定义的方法。

      应该可以的

      if (list is Dictionary<int, string>) {
          // do special case
      }
      

      但我一想到就发抖。

      您甚至可以进行一般检查:

      class Printer<T>
      {
          public Printer<T>(IEnumerable list)
          {
              foreach (var enumerable in list)
              {
                  if (list is Dictionary<T, T>) {
                      //DO STUFF
                  }
              }
          }
      }
      

      【讨论】:

      • 我也为这个想法不寒而栗
      • 是否有办法检查列表是否是字典?
      【解决方案5】:

      问题在于,集合虽然是可枚举的,但可以容纳不同类型的对象,正如您所看到的 List 和 Dictionary 之间的区别。

      要在不为每个对象类型编码的情况下解决此问题,您必须只接受您定义的特定类型的可枚举集合,例如 IEnumerable&lt;IMyType&gt;。

      【讨论】:

        【解决方案6】:

        如果您在被调用者处无法执行任何操作,则由调用者确保它传递一个对打印机“有效”的IEnumerable,例如在您的示例中传递dictionary.Values 而不是dictionary。但是,如果该类是公开的并且将被第 3 方用户使用,您最好像其他人所说的那样,为您的 IEnumerable 添加一些通用约束。

        【讨论】:

          【解决方案7】:

          结果如下: 我使用了你们的帮助,所以我想我不应该投票给我自己的答案。

          class Printer
          {
              public Printer(IEnumerable<IPrintable> list) //Accepts any collection with an object that implements IPrintable interface
              {
                  foreach (var enumerable in list) //iterate through list of objects
                  {
                      foreach (var printable in enumerable)//loops through properties in current object
                      {
                          //DO STUFF 
                      }
                  }
              }
          }
          
          interface IPrintable : IEnumerable { }
          class SomeObject : IPrintable
          {
              public string Property1 { get; set; }
              public string Property2 { get; set; }
          
              public interface IEnumerable
              {
                  IEnumerator GetEnumerator(); //Returns a Enumerator
              }
          
              public IEnumerator GetEnumerator()
              {
                  yield return Property1;
                  yield return Property2;
              }
          }
          

          我自然需要为每个对象实现自定义 GetEnumerator() - 不过没问题!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-04-12
            • 1970-01-01
            • 1970-01-01
            • 2015-08-21
            • 1970-01-01
            • 2020-10-10
            相关资源
            最近更新 更多