如何改进系统.具有收缩,toArray和fromArray方法的元组

本文关键字:toArray fromArray 方法 元组 系统 何改进 | 更新日期: 2023-09-27 18:13:32

我需要实现一个方法regards System。. net中的元组类。最好的方法是什么?扩展方法,子类的Tuple类的整个集合或创建一个新的类基于源代码的System.Tuple?

该方法输入一个Tuple<T1,T2..Tn>,并根据掩码(int或boolean)返回一个Tuple<T1..Tm> (m<n)。如何替换switch-case语句和Reflections的使用?我怎样才能提高性能?

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)

如何改进系统.具有收缩,toArray和fromArray方法的元组

这可能不会提高函数的运行时间,但它可以避免切换情况。

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>且length = 3,则输出类型将为Tuple<int,int,string>

为了避免反射,您唯一的选择是为所有Tuple, Tuple..创建重载。泛型元组类型:

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的返回类型仍然是"对象",因为返回的是不同的元组,而且它们不共享任何可用的公共类或接口。你为什么要这么做呢?我有一种感觉,你可能不需要为你的任务使用Tuple类。

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)如果它适合,您可以返回动态决定定义的动态类型。比如:

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)最后,你应该能够使用表达式树和使用预编译委托而不是反射来生成运行时元组,但由于这整个事情不是通用的,你将不得不使用DynamicInvoke,这将是较慢的,所以我不确定性能增益。DynamicInvoke非常慢,是框架中最慢的结构之一。如果性能很重要,可以像Evk所示的那样编写单独的重载。