问题仍然不清楚,但我认为你想将你的
a
集合转换成以下形式的值元组:
(a[0], a[1], a[2], …)
。这不能通过任何内置功能实现。此外,您会很快遇到限制,因为.NET Framework仅定义了高达
ValueTuple<T1, …, T7>
——超出这个范围需要使用
ValueTuple<T1, …, T7, TRest>
构造笨重的嵌套值元组(例如
ValueTuple<T1, …, T7, ValueTuple<T1, …, T7, ValueTuple<T1, …>>>
)。
如果你想实现集合相等比较,你应该使用
IEqualityComparer<ICollection<T>>
。下面是来自
SequenceEqualityComparer
的一个样例实现:
。
public class SequenceEqualityComparer<TElement> : EqualityComparer<IEnumerable<TElement>>
{
private readonly IEqualityComparer<TElement> _elementEqualityComparer;
public SequenceEqualityComparer()
: this(null)
{ }
public SequenceEqualityComparer(IEqualityComparer<TElement> elementEqualityComparer)
{
_elementEqualityComparer = elementEqualityComparer ?? EqualityComparer<TElement>.Default;
}
public new static SequenceEqualityComparer<TElement> Default { get; } = new SequenceEqualityComparer<TElement>();
public override bool Equals(IEnumerable<TElement> x, IEnumerable<TElement> y)
{
if (object.ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
if (x is ICollection<TElement> xCollection &&
y is ICollection<TElement> yCollection &&
xCollection.Count != yCollection.Count)
return false;
return x.SequenceEqual(y, _elementEqualityComparer);
}
public override int GetHashCode(IEnumerable<TElement> sequence)
{
if (sequence == null)
return 0;
unchecked
{
const uint fnvPrime = 16777619;
uint hash = 2166136261;
foreach (uint item in sequence.Select(_elementEqualityComparer.GetHashCode))
hash = (hash ^ item) * fnvPrime;
return (int)hash;
}
}
}
编辑:为了好玩,这是我对你的实际问题使用反射和递归的实现:
public static object CreateValueTuple<T>(ICollection<T> collection)
{
object[] items;
Type[] parameterTypes;
if (collection.Count <= 7)
{
items = collection.Cast<object>().ToArray();
parameterTypes = Enumerable.Repeat(typeof(T), collection.Count).ToArray();
}
else
{
var rest = CreateValueTuple(collection.Skip(7).ToArray());
items = collection.Take(7).Cast<object>().Append(rest).ToArray();
parameterTypes = Enumerable.Repeat(typeof(T), 7).Append(rest.GetType()).ToArray();
}
var createMethod = typeof(ValueTuple).GetMethods()
.Where(m => m.Name == "Create" && m.GetParameters().Length == items.Length)
.SingleOrDefault() ?? throw new NotSupportedException("ValueTuple.Create method not found.");
var createGenericMethod = createMethod.MakeGenericMethod(parameterTypes);
var valueTuple = createGenericMethod.Invoke(null, items);
return valueTuple;
}
使用示例:
var collection = new[] { 5, 6, 6, 2, 8, 4, 6, 2, 6, 8, 3, 6, 3, 7, 4, 1, 6 };
var valueTuple = CreateValueTuple(collection);
如果你不介意将Item8
装箱,你可以摆脱反射:
public static object CreateValueTuple<T>(IList<T> list)
{
switch (list.Count)
{
case 0: return default(ValueTuple);
case 1: return (list[0]);
case 2: return (list[0], list[1]);
case 3: return (list[0], list[1], list[2]);
case 4: return (list[0], list[1], list[2], list[3]);
case 5: return (list[0], list[1], list[2], list[3], list[4]);
case 6: return (list[0], list[1], list[2], list[3], list[4], list[5]);
case 7: return (list[0], list[1], list[2], list[3], list[4], list[5], list[6]);
default: return (list[0], list[1], list[2], list[3], list[4], list[5], list[6], CreateValueTuple(list.Skip(7).ToList()));
}
}
区别在于基于反射的方法会生成以下类型的结果:
ValueTuple<int,int,int,int,int,int,int,ValueTuple<ValueTuple<int,int,int,int,int,int,int,ValueTuple<ValueTuple<int,int,int>>>>>
...而基于交换机的方法会产生:
ValueTuple<int,int,int,int,int,int,int,ValueTuple<object>>
在每种情况下,都会有一个多余的单组件ValueTuple<T>
包装嵌套的值元组。这是.NET Framework的ValueTuple.Create<T1, …, T8>
方法实现的一个不幸的设计缺陷,即使使用值元组语法(例如(1, 2, 3, 4, 5, 6, 7, (8, 9))
),也会出现这种情况。
public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
{
return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>>(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8));
}
ValueTuple<T1, …, T7, TRest>()构造函数来解决此问题,如
他们的答案所示。
(myInt, myString)
。 - DouglasValueTuple
的目的完全不同。如果您只想窃取实现多个值上的.Equals()
和.GetHashCode()
的代码,尽管拿去 -- 将这样的代码适应到集合上并不特别复杂。这肯定比尝试动态创建ValueTuple
更加简洁,对于超过7个项目,这种方法会变得非常不舒服。毫无疑问,也有很多库以某种方式实现了这一点(例如用于单元测试)。 - Jeroen Mostert