如何使用 shrink、toArray 和 fromArray 方法改进 System.Tuple

How to improve System.Tuple with shrink, toArray and fromArray methods

我需要在 .Net 中实现一个关于 System.Tuple class 的方法。最好的方法是什么?扩展方法,subclass 整套 Tuple classes 或基于 System.Tuple?

的源代码创建一个新的 class

该方法根据掩码(整数或布尔值)输入 Tuple<T1,T2..Tn> 和 return Tuple<T1..Tm> (m<n)。如何替换 switch-case 语句和反射的使用?我怎样才能提高性能?

public static object shrink(this object source, params int[] mask)
{
    PropertyInfo[] prop = source.GetType().GetRuntimeProperties().ToArray<PropertyInfo>();
    object[] tmp = new object[mask.Length];
    int index = 0;
    for (int i = 0; i < mask.Length; i++)
    {
        if (mask[i] != 0)
        {
            tmp[index] = prop[i].GetValue(source, null);
            index++;
        }
    }
    switch (index)
    {
        case 1: return Tuple.Create(tmp[0]);
        case 2: return Tuple.Create(tmp[0], tmp[1]);
            ....
            ....
    }
}

以同样的方式,我需要实现一个 Tuple 到数组和一个数组到 Tuple 的方法。

2) public static object toArray(this object source)

输入:Tuple<T1..Tn>输出:object[]

3) public static object FromArray(this object source)

输入:object[]输出Tuple<T1..Tn)

为避免反射,您唯一的选择是为所有元组、元组...通用元组类型创建重载:

public static object shrink<T1>(this Tuple<T1> tuple, params int[] mask) {
}

public static object shrink<T1, T2>(this Tuple<T1, T2> tuple, params int[] mask) {
}

...

ToArray() 方法相同。 FromArray 和 Shrink 的 Return 类型将保持 "object" 无论如何,因为你 return 不同的元组,并且它们不共享任何可用的 public class 或接口。你为什么需要这样做?我觉得您可能不需要使用 Tuple classes 来完成您的任务。

这可能不会提高函数的运行时间,但会避免 switch case。

private object StripTuple(object tuple, int[] mask)
{
    int[] indexes = mask.Select((v, i) => new { val = v, index = i }).Where(o => o.val == 1).Select(i => i.index).ToArray();
    object[] items = new object[indexes.Length];
    Type[] tupleTypes = new Type[indexes.Length];
    var originalTupleType = tuple.GetType();
    var genericArgs = originalTupleType.GetGenericArguments();

    for (var i = 0; i < indexes.Length; i++)
    {
        items[i] = originalTupleType.GetProperty("Item" + (indexes[i] + 1)).GetValue(tuple);
        tupleTypes[i] = genericArgs[indexes[i]];
    }

    Type tupleType = Type.GetType("System.Tuple`" + indexes.Length);
    Type newTupleType = tupleType.MakeGenericType(tupleTypes);
    return Activator.CreateInstance(newTupleType, items);
}

用法:

var t1 = new Tuple<int, int, int, string, double, string, int>(1, 2, 3, "4", 5.7, "6", 7);
var newTuple = StripTuple(t1, new int[7]{0,0,0,1,1,0,1}); // "4",5.7,7

该函数将采用前 m 个参数(m <= 元组的长度)。
如果输入为 Tuple<int,int,string,int> 且长度 = 3,则输出类型将为 Tuple<int,int,string>

1) 这与 Amir 的回答相同,只是更像 LINQ。

public static object shrink(this object source, params int[] mask)
{
    var props = source.GetType().GetProperties();
    var values = mask
                .Select((val, index) => new { val, index })
                .Where(x => x.val != 0)
                .Select(x => props[x.index])
                .Select(prop => new { prop, value = prop.GetValue(source) })
                .ToList();

    var type = Type.GetType("System.Tuple`" + values.Count)
              .MakeGenericType(values.Select(x => x.prop.PropertyType).ToArray());
    var arguments = values.Select(x => x.value).ToArray();
    return Activator.CreateInstance(type, arguments);
}

2) 如果合适,您可以 return 动态决定定义的动态类型。类似于:

public static object shrink(this object source, params int[] mask)
{
    var props = source.GetType().GetProperties();
    var items = mask
               .Select((val, index) => new { val, index })
               .Where(x => x.val != 0)
               .Select((x, index) => new
               {
                   name = "Item" + index,
                   val = props[x.index].GetValue(source)
               })
               .ToList();

    var expando = new ExpandoObject() as IDictionary<string, object>;
    foreach (var item in items)
        expando[item.name] = item.val;

    return expando;
}

3) 最后,您应该能够使用表达式树并使用预编译委托而不是反射来生成 运行-time 元组,但由于这整个事情不是通用的,您将不得不使用DynamicInvoke 这会更慢,所以我不确定性能提升。 DynamicInvokes 非常慢,并且是框架中最慢的构造之一。如果性能很重要,您可以编写单独的重载,如 Evk 所示。