【问题标题】:Convert ValueTuple to IEnumerable将 ValueTuple 转换为 IEnumerable
【发布时间】:2017-10-15 20:42:08
【问题描述】:

是否有更明智的方法来执行以下操作:

public static class ValueTupleAdditions {
  public static IEnumerable<object> ToEnumerable<A, B>(this ValueTuple<A, B> tuple) {
    yield return tuple.Item1;
    yield return tuple.Item2;
  }
  public static IEnumerable<object> ToEnumerable<A, B, C>(this ValueTuple<A, B, C> tuple) {
    yield return tuple.Item1;
    yield return tuple.Item2;
    yield return tuple.Item3;
  }

  [etc]
}

编辑:既然人们要求一个用例,那就给你。

using Xunit;

namespace Whatever {

  public class SomeTestClass {
    public static IEnumerable<(string, Expression<Func<string, string>>, string)> RawTestData() {
      yield return ("Hello", str => str.Substring(3), "lo");
      yield return ("World", str => str.Substring(0, 4), "worl");
    }
    public static IEnumerable<object[]> StringTestData() {
      return RawTestData().Select(vt => new object[] { vt.Item1, vt.Item2, vt.Item3 });
       // would prefer to call RawTestData().Select(vt => vt.ToArray()) here, but it doesn't exist.
    }

    [Theory, MemberData(nameof(StringTestData))]
    public void RunStringTest(string input, Expression<Func<string, string>> func, string expectedOutput) {
      var output = func.Compile()(input);
      Assert.Equal(expectedOutput, output);
    }
  }
}

【问题讨论】:

  • 好吧,你可以创建一个数组......但实际上,我想说,这是一个相当不寻常的元组用法。你有具体的用例吗?
  • 是的,通过理论进行 xunit 测试,您确实想要初始输入的值元组,您将它们转换为对象数组以使 xunit 满意,然后在测试方法中将它们作为原始类型提取出来
  • 什么?发布您的测试代码。
  • IEnumerable &lt;object[]&gt; 是一个可枚举的测试方法参数?这里没有显式转换?
  • 0 反对票似乎您想将元组转换为它不是的东西。这完全没问题,但也许你可以为此创建一个类,它具有所有 8 个元组形式的构造函数。这样您就可以让该类枚举、切片、计数,将其与另一个元组组合,将其转换回(较小的)元组等等。

标签: c# ienumerable valuetuple


【解决方案1】:

一种方法是使用基于ITuple的扩展方法,另见answer by Julien Couvreur

public static IEnumerable<T> ToEnumerable<T>( this ITuple tuple ) {
    for ( var n = 0; n < tuple.Length; n++ ) yield return (T)tuple[ n ];
}

示例用法:

var directions = (
    right: (cx: 1, cy: 0),
    down: (cx: 0, cy: 1),
    left: (cx: -1, cy: 0),
    up: (cx: 0, cy: -1)
);
foreach ( var direction in directions.ToEnumerable<(int cx, int cy)>() ) {
    var (cx, cy) = direction;
    TryMovePiece( (x + cx, y + cy) );
}

【讨论】:

    【解决方案2】:

    一种方法是通过ITuple interface

    public interface ITuple
    {
        int Length { get; }
        object this[int index] { get; }
    }
    

    仅在 .NET Core 2.0、Mono 5.0 和 .NET Framework 的下一版本(未发布,4.7 之后)中可用。 它不(也永远不会)通过 ValueTuple 包作为旧框架的附加组件提供。

    此 API 旨在供 C# 编译器用于未来的模式工作。

    【讨论】:

      【解决方案3】:

      一点反思:

      namespace ConsoleApp1
      {
          using System;
          using System.Collections.Generic;
          using System.Linq;
      
          public class Program
          {
              public static void Main()
              {
                  var tuple = (1, 2, 3, 4, 5, 6, 7);
                  var items = ToEnumerable(tuple);
      
                  foreach (var item in items)
                  {
                      Console.WriteLine(item);
                  }
              }
      
              private static IEnumerable<object> ToEnumerable(object tuple)
              {
                  if (tuple.GetType().GetInterface("ITupleInternal") != null)
                  {
                      foreach (var prop in tuple.GetType()
                          .GetFields()
                          .Where(x => x.Name.StartsWith("Item")))
                      {
                          yield return prop.GetValue(tuple);
                      }
                  }
                  else
                  {
                      throw new ArgumentException("Not a tuple!");
                  }
              }
          }
      }
      

      【讨论】:

      • 谢谢!
      • 引用Type.GetFields: "GetFields 方法不返回特定顺序的字段,例如字母顺序或声明顺序。您的代码不能依赖于字段的顺序返回,因为顺序不同。“因此,我们可能应该从字段名称中提取项目编号并OrderBy它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多