TKey は、作成可能なSystem.Tupleタイプの 1 つです。
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 は、作成可能なSystem.Tupleタイプの 1 つです。
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);
}
}
}
私はこれが古いものであることを知っていますが、これがそれに出くわす可能性のある人々の助けになることを願っています:)
そのようです:
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 のジェネリック パラメータをクラスに追加します。
以下のコメントによると、これをインターフェイスやリフレクションなしで 1 つのクラスに結合する方法はありません。もしあれば、複数の Tuple クラスはありませんでした
しかし... 複数の Class1 によって継承される独自の基本インターフェイスを定義し、そのインターフェイスを制約として使用できます。きれいに聞こえませんが、うまくいきます