我想创建一个具有一个通用 TKey 的类,其中 TKey 是可以创建的System.Tuple类型之一。
public class Class1<TKey> where TKey : System.Tuple { /// Class Stuff Goes Here where TKey is one of the 8 tuple types found in the link in (1) }
我不太确定如何实现这一点。目标是防止自己为每个元组类实现一个类。
我想创建一个具有一个通用 TKey 的类,其中 TKey 是可以创建的System.Tuple类型之一。
public class Class1<TKey> where TKey : System.Tuple
{
/// Class Stuff Goes Here where TKey is one of the 8 tuple
types found in the link in (1)
}
我不太确定如何实现这一点。目标是防止自己为每个元组类实现一个类。
你不能像其他人所说的那样,但你几乎可以做到。
所以所有的Tuple<...>
类都有这样的签名:
public class Tuple<T1, ...> :
IStructuralEquatable,
IStructuralComparable,
IComparable,
ITuple
所有这些接口保存ITuple
都是公共的(ITuple
是一个内部接口),所以你可以尝试制作这样的东西:
public interface ITupleKey<TKey>
where TKey : IStructuralEquatable, IStructuralComparable, IComparable
{
}
“但是等等!”,你说,“我怎么能确定没有其他东西正在实现这些接口?”
好吧,你不能。但就像我说的,这只是一种几乎的方式——幸运的是,IStructuralEquatable
并且IStructuralComparable
仅(在框架级别,自然地)用于以下类型:
System.Array
System.Tuple<T1>
System.Tuple<T1,T2>
System.Tuple<T1,T2,T3>
System.Tuple<T1,T2,T3,T4>
System.Tuple<T1,T2,T3,T4,T5>
System.Tuple<T1,T2,T3,T4,T5,T6>
System.Tuple<T1,T2,T3,T4,T5,T6,T7>
System.Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>
所以它非常接近。TKey
将此与实际上是 的某种变体的运行时检查结合起来Tuple
,您可能会拥有所需的东西。
编辑:
一些基本用法:
public class Class1<TKey>
where TKey : IStructuralEquatable, IStructuralComparable, IComparable
{
}
// will compile
var classTup1 = new Class1<Tuple<int>>();
var classTup2 = new Class1<Tuple<int,int>>();
var classTup3 = new Class1<Tuple<int,int,int>>();
var classTup4 = new Class1<Tuple<int,int,int,int>>();
var classTup5 = new Class1<Tuple<int,int,int,int,int>>();
// won't compile
var badclassTup1 = new Class1<int>();
var badclassTup2 = new Class1<string>();
var badclassTup3 = new Class1<object>();
而且,因为我显然已经疯了,让我们看看这里有什么可能:
public class Class1<TKey>
where TKey : IStructuralEquatable, IStructuralComparable, IComparable
{
public Class1(TKey key)
{
Key = key;
TupleRank = typeof(TKey).GetGenericArguments().Count();
TupleSubtypes = typeof(TKey).GetGenericArguments();
Console.WriteLine("Key type is a Tuple (I think) with {0} elements", TupleRank);
TupleGetters =
Enumerable.Range(1, TupleRank)
.Select(i => typeof(TKey).GetProperty(string.Concat("Item",i.ToString())))
.Select(pi => pi.GetGetMethod())
.Select(getter => Delegate.CreateDelegate(
typeof(Func<>).MakeGenericType(getter.ReturnType),
this.Key,
getter))
.ToList();
}
public int TupleRank {get; private set;}
public IEnumerable<Type> TupleSubtypes {get; private set;}
public IList<Delegate> TupleGetters {get; private set;}
public TKey Key {get; private set;}
public object this[int rank]
{
get { return TupleGetters[rank].DynamicInvoke(null);}
}
public void DoSomethingUseful()
{
for(int i=0; i<TupleRank; i++)
{
Console.WriteLine("Key value for {0}:{1}", string.Concat("Item", i+1), this[i]);
}
}
}
试验台:
var classTup1 = new Class1<Tuple<int>>(Tuple.Create(1));
var classTup2 = new Class1<Tuple<int,int>>(Tuple.Create(1,2));
var classTup3 = new Class1<Tuple<int,int,int>>(Tuple.Create(1,2,3));
var classTup4 = new Class1<Tuple<int,int,int,int>>(Tuple.Create(1,2,3,4));
var classTup5 = new Class1<Tuple<int,int,int,int,int>>(Tuple.Create(1,2,3,4,5));
classTup1.DoSomethingUseful();
classTup2.DoSomethingUseful();
classTup3.DoSomethingUseful();
classTup4.DoSomethingUseful();
classTup5.DoSomethingUseful();
输出:
Key type is a Tuple (I think) with 1 elements
Key type is a Tuple (I think) with 2 elements
Key type is a Tuple (I think) with 3 elements
Key type is a Tuple (I think) with 4 elements
Key type is a Tuple (I think) with 5 elements
Key value for Item1:1
Key value for Item1:1
Key value for Item2:2
Key value for Item1:1
Key value for Item2:2
Key value for Item3:3
Key value for Item1:1
Key value for Item2:2
Key value for Item3:3
Key value for Item4:4
Key value for Item1:1
Key value for Item2:2
Key value for Item3:3
Key value for Item4:4
Key value for Item5:5
我很惊讶没有人建议你基于ITuple
接口约束你的类(或者在我的情况下是一个方法)。为了说明您的示例:
public class Class1<T> where T : ITuple
您需要引用System.Runtime.CompilerServices
程序集。
我的特殊用例是我想通过一组未知形状的元组进行迭代。我的解决方案是:
public static void ProcessTupleCollection<T>(this IEnumerable<T> collection) where T: ITuple
{
var type = typeof(T);
var fields = type.GetFields();
foreach (var item in collection)
{
foreach (var field in fields)
{
field.GetValue(item);
}
}
}
我知道这是一个旧的,但我希望这可以帮助那些可能偶然发现它的人:)
你不能这样做,有两个原因。
首先,您不能对泛型类型进行“或”约束。您可能必须对Tuple<T1, T2>
or Tuple<T1, T2, T3>
有一个约束。这是不可能的。
其次,您需要“通过”通用参数,如下所示:
public class Class1<TKey, T1, T2> where TKey : System.Tuple<T1, T2>
因此,您的类需要可变数量的泛型类型参数,这也不支持。
除非有特定原因TKey
绝对必须是元组类之一,否则不要限制它。
你不能。Tuple 的不同泛型变体没有合理的通用基类或接口。
泛型约束的目的是告诉编译器泛型类型应该有哪些成员。
像这样:
public class Class1<TKey,P,Q> where TKey : System.Tuple<P,Q>
{
/// Class Stuff Goes Here where TKey is one of the 8 tuple
types found in the link in (1)
}
将 Tuple 的泛型参数添加到您的类中'
根据下面的评论,没有接口或反射就无法将其组合成一个类。如果有就不会有多个元组类
但是...您可以定义自己的由多个 Class1 继承的基本接口,然后将该接口用作约束。听起来不漂亮,但会工作