C# 7.0 でタプル(ValueTuple)を自作してみる。

こんにちは、Takymです。
今回は、System.VslueTupleをNuGetからダウンロードせずに、自分で実装しようと思います。

目次

メリット・デメリット

メリット

  • 好きな機能を実現できる。
  • System.VslueTupleの仕組みが分かる。
  • 外部参照じゃないから高速。(ライブラリにしたら意味がない)

デメリット

  • 関数が多くて実装がめんどくさい。
  • 公式じゃないから、バグが出る可能性がある。

ValueTupleの実装

MSDNに載っていました。
https://msdn.microsoft.com/ja-jp/library/system.valuetuple(v=vs.110).aspx

とりあえず実装

まず最初に以下の様に実装してみました。
ソースコードをスキップする。

namespace System
{
public class ValueTuple
{
public int FieldCount { get; protected set; }
public ValueTuple()
{
FieldCount = 0;
}
public override bool Equals(object obj)
{
ValueTuple v = obj as ValueTuple;
if (v == null) {
return false;
}
return this.FieldCount == v.FieldCount;
}
public override int GetHashCode()
{
return 0;
}
public override string ToString()
{
return "{ \"FieldCount\":0 }";
}
public static bool operator ==(ValueTuple left, ValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple left, ValueTuple right)
{
return !left.Equals(right);
}
}
public class ValueTuple<T1> : ValueTuple
{
public T1 Item1;
public ValueTuple()
{
this.FieldCount = 1;
}
public ValueTuple(T1 item1)
{
this.FieldCount = 1;
this.Item1 = item1;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1>) {
ValueTuple<T1> v = ((ValueTuple<T1>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\" }}";
}
public void Deconstruct(out T1 item1)
{
item1 = this.Item1;
}
}
public class ValueTuple<T1, T2> : ValueTuple
{
public T1 Item1;
public T2 Item2;
public ValueTuple()
{
this.FieldCount = 2;
}
public ValueTuple(T1 item1, T2 item2)
{
this.FieldCount = 2;
this.Item1 = item1;
this.Item2 = item2;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2>) {
ValueTuple<T1, T2> v = ((ValueTuple<T1, T2>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2)
{
item1 = this.Item1;
item2 = this.Item2;
}
}
public class ValueTuple<T1, T2, T3> : ValueTuple
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public ValueTuple()
{
this.FieldCount = 3;
}
public ValueTuple(T1 item1, T2 item2, T3 item3)
{
this.FieldCount = 3;
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3>) {
ValueTuple<T1, T2, T3> v = ((ValueTuple<T1, T2, T3>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
}
}
public class ValueTuple<T1, T2, T3, T4> : ValueTuple
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public ValueTuple()
{
this.FieldCount = 4;
}
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4)
{
this.FieldCount = 4;
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4>) {
ValueTuple<T1, T2, T3, T4> v = ((ValueTuple<T1, T2, T3, T4>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
}
}
public class ValueTuple<T1, T2, T3, T4, T5> : ValueTuple
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public ValueTuple()
{
this.FieldCount = 5;
}
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
{
this.FieldCount = 5;
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5>) {
ValueTuple<T1, T2, T3, T4, T5> v = ((ValueTuple<T1, T2, T3, T4, T5>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
}
}
public class ValueTuple<T1, T2, T3, T4, T5, T6> : ValueTuple
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public T6 Item6;
public ValueTuple()
{
this.FieldCount = 6;
}
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
{
this.FieldCount = 6;
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
this.Item6 = item6;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5, T6>) {
ValueTuple<T1, T2, T3, T4, T5, T6> v = ((ValueTuple<T1, T2, T3, T4, T5, T6>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5)
&& this.Item6.Equals(v.Item6);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode() ^ this.Item6.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\", " +
$"\"Item6\":\"{this.Item6}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
item6 = this.Item6;
}
}
public class ValueTuple<T1, T2, T3, T4, T5, T6, T7> : ValueTuple
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public T6 Item6;
public T7 Item7;
public ValueTuple()
{
this.FieldCount = 7;
}
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
{
this.FieldCount = 7;
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
this.Item6 = item6;
this.Item7 = item7;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5, T6, T7>) {
ValueTuple<T1, T2, T3, T4, T5, T6, T7> v = ((ValueTuple<T1, T2, T3, T4, T5, T6, T7>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5)
&& this.Item6.Equals(v.Item6)
&& this.Item7.Equals(v.Item7);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode() ^ this.Item6.GetHashCode() ^ this.Item7.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\", " +
$"\"Item6\":\"{this.Item6}\", \"Item7\":\"{this.Item7}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
item6 = this.Item6;
item7 = this.Item7;
}
}
public class ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn> : ValueTuple where Tn: ValueTuple, new()
{
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public T6 Item6;
public T7 Item7;
public Tn ItemN;
public ValueTuple()
{
this.FieldCount = 7 + new Tn().FieldCount;
}
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, Tn itemN)
{
this.FieldCount = 7;
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
this.Item6 = item6;
this.Item7 = item7;
this.ItemN = itemN;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn>) {
ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn> v = ((ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5)
&& this.Item6.Equals(v.Item6)
&& this.Item7.Equals(v.Item7)
&& this.ItemN.Equals(v.ItemN);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode() ^ this.Item6.GetHashCode() ^ this.Item7.GetHashCode() ^ this.ItemN.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\", " +
$"\"Item6\":\"{this.Item6}\", \"Item7\":\"{this.Item7}\", \"ItemN\":{this.ItemN} }}";
}
public void Deconstruct(
out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out Tn itemN)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
item6 = this.Item6;
item7 = this.Item7;
itemN = this.ItemN;
}
}
}


しかし、コンパイルに失敗しました。
どうやら、ValueTupleは構造体でなければいけないようです。
因みに、FieldCountはタプルのフィールドの数です。
Equalsメソッドで、タプルの値が同じかどうか、計算できるようにしています。
GetHashCodeメソッドはEqualsメソッドの為に実装しています。
戻り値はアイテムの値から作成するようにしています。
ToStringメソッドはアイテムの値をJSON形式にしています。これは僕の趣味です。
(※JSONをタプルに変換するコードは含まれていません。)
Deconstructメソッドはタプルを分解するためのメソッドです。
ValueTupleは、アイテムが1個の時のタプルです。
一個増えるにつれて、Itemを一つずつ増やしています。
ValueTupleは、アイテムが8個以上の時のタプルです。
TnはValueTuple型を継承した型のみ使用できます。
つまり、Tnに別のValueTupleを含む事ができます。
なので、理論上無限のタプルを生成する事ができます。
Createメソッドが無いのは手抜きです。

正しい実装

classの部分をstructに変えて少し修正しました。
ソースコードをスキップする。

namespace System
{
public interface IValueTuple
{
int FieldCount { get; }
}
public struct ValueTuple : IValueTuple
{
public int FieldCount
{
get
{
return 0;
}
}
public override bool Equals(object obj)
{
if (obj == null) {
return false;
}
if (obj is ValueTuple) {
ValueTuple v = ((ValueTuple)(obj));
return this.FieldCount == v.FieldCount;
} else {
return false;
}
}
public override int GetHashCode()
{
return 0;
}
public override string ToString()
{
return "{ \"FieldCount\":0 }";
}
public static bool operator ==(ValueTuple left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1> : IValueTuple
{
public int FieldCount
{
get
{
return 1;
}
}
public T1 Item1;
public ValueTuple(T1 item1)
{
this.Item1 = item1;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1>) {
ValueTuple<T1> v = ((ValueTuple<T1>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\" }}";
}
public void Deconstruct(out T1 item1)
{
item1 = this.Item1;
}
public static bool operator ==(ValueTuple<T1> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2> : IValueTuple
{
public int FieldCount
{
get
{
return 2;
}
}
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2>) {
ValueTuple<T1, T2> v = ((ValueTuple<T1, T2>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2)
{
item1 = this.Item1;
item2 = this.Item2;
}
public static bool operator ==(ValueTuple<T1, T2> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2, T3> : IValueTuple
{
public int FieldCount
{
get
{
return 3;
}
}
public T1 Item1;
public T2 Item2;
public T3 Item3;
public ValueTuple(T1 item1, T2 item2, T3 item3)
{
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3>) {
ValueTuple<T1, T2, T3> v = ((ValueTuple<T1, T2, T3>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
}
public static bool operator ==(ValueTuple<T1, T2, T3> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2, T3> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2, T3, T4> : IValueTuple
{
public int FieldCount
{
get
{
return 4;
}
}
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4)
{
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4>) {
ValueTuple<T1, T2, T3, T4> v = ((ValueTuple<T1, T2, T3, T4>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
}
public static bool operator ==(ValueTuple<T1, T2, T3, T4> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2, T3, T4> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2, T3, T4, T5> : IValueTuple
{
public int FieldCount
{
get
{
return 5;
}
}
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
{
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5>) {
ValueTuple<T1, T2, T3, T4, T5> v = ((ValueTuple<T1, T2, T3, T4, T5>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
}
public static bool operator ==(ValueTuple<T1, T2, T3, T4, T5> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2, T3, T4, T5> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2, T3, T4, T5, T6> : IValueTuple
{
public int FieldCount
{
get
{
return 6;
}
}
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public T6 Item6;
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
{
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
this.Item6 = item6;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5, T6>) {
ValueTuple<T1, T2, T3, T4, T5, T6> v = ((ValueTuple<T1, T2, T3, T4, T5, T6>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5)
&& this.Item6.Equals(v.Item6);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode() ^ this.Item6.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\", " +
$"\"Item6\":\"{this.Item6}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
item6 = this.Item6;
}
public static bool operator ==(ValueTuple<T1, T2, T3, T4, T5, T6> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2, T3, T4, T5, T6> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2, T3, T4, T5, T6, T7> : IValueTuple
{
public int FieldCount
{
get
{
return 7;
}
}
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public T6 Item6;
public T7 Item7;
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
{
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
this.Item6 = item6;
this.Item7 = item7;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5, T6, T7>) {
ValueTuple<T1, T2, T3, T4, T5, T6, T7> v = ((ValueTuple<T1, T2, T3, T4, T5, T6, T7>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5)
&& this.Item6.Equals(v.Item6)
&& this.Item7.Equals(v.Item7);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode() ^ this.Item6.GetHashCode() ^ this.Item7.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\", " +
$"\"Item6\":\"{this.Item6}\", \"Item7\":\"{this.Item7}\" }}";
}
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
item6 = this.Item6;
item7 = this.Item7;
}
public static bool operator ==(ValueTuple<T1, T2, T3, T4, T5, T6, T7> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2, T3, T4, T5, T6, T7> left, IValueTuple right)
{
return !left.Equals(right);
}
}
public struct ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn> : IValueTuple where Tn: IValueTuple, new()
{
public int FieldCount
{
get
{
return 7 + ItemN.FieldCount;
}
}
public T1 Item1;
public T2 Item2;
public T3 Item3;
public T4 Item4;
public T5 Item5;
public T6 Item6;
public T7 Item7;
public Tn ItemN;
public Tn Rest
{
get
{
return this.ItemN;
}
set
{
this.ItemN = value;
}
}
public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, Tn itemN)
{
this.Item1 = item1;
this.Item2 = item2;
this.Item3 = item3;
this.Item4 = item4;
this.Item5 = item5;
this.Item6 = item6;
this.Item7 = item7;
this.ItemN = itemN;
}
public override bool Equals(object obj)
{
if (obj is ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn>) {
ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn> v = ((ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn>)(obj));
if (this.FieldCount == v.FieldCount) {
return this.Item1.Equals(v.Item1)
&& this.Item2.Equals(v.Item2)
&& this.Item3.Equals(v.Item3)
&& this.Item4.Equals(v.Item4)
&& this.Item5.Equals(v.Item5)
&& this.Item6.Equals(v.Item6)
&& this.Item7.Equals(v.Item7)
&& this.ItemN.Equals(v.ItemN);
} else {
return false;
}
} else {
return false;
}
}
public override int GetHashCode()
{
return this.Item1.GetHashCode() ^ this.Item2.GetHashCode() ^ this.Item3.GetHashCode() ^ this.Item4.GetHashCode()
^ this.Item5.GetHashCode() ^ this.Item6.GetHashCode() ^ this.Item7.GetHashCode() ^ this.ItemN.GetHashCode();
}
public override string ToString()
{
return $"{{ \"FieldCount\":{this.FieldCount}, \"Item1\":\"{this.Item1}\", \"Item2\":\"{this.Item2}\", " +
$"\"Item3\":\"{this.Item3}\", \"Item4\":\"{this.Item4}\", \"Item5\":\"{this.Item5}\", " +
$"\"Item6\":\"{this.Item6}\", \"Item7\":\"{this.Item7}\", \"ItemN\":{this.ItemN} }}";
}
public void Deconstruct(
out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out Tn itemN)
{
item1 = this.Item1;
item2 = this.Item2;
item3 = this.Item3;
item4 = this.Item4;
item5 = this.Item5;
item6 = this.Item6;
item7 = this.Item7;
itemN = this.ItemN;
}
public static bool operator ==(ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn> left, IValueTuple right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTuple<T1, T2, T3, T4, T5, T6, T7, Tn> left, IValueTuple right)
{
return !left.Equals(right);
}
}
}


構造体はクラスを継承する事ができないので、インターフェースを使う事にしました。
また、公式の実装を見ると、ItemNはRestになっているようなので、プロパティでItemNを参照できるようにしました。

使用例

以下のプログラムを実行してみます。

using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("{0}",
("string",      123,             true,              1.5,
"hogehoge",    new object(),    new DateTime(),    "test",
false,         0,               3.2,               987,
"ABC123",      "a1b2"));
Console.ReadLine();
}
}
}

実行したら、以下の様な表示になる筈です。

{
"FieldCount":14,
"Item1":"string",
"Item2":"123",
"Item3":"True",
"Item4":"1.5",
"Item5":"hogehoge",
"Item6":"System.Object",
"Item7":"0001/01/01 00:00:00",
"ItemN":{
"FieldCount":7,
"Item1":"test",
"Item2":"False",
"Item3":"0",
"Item4":"3.2",
"Item5":"987",
"Item6":"ABC123",
"Item7":"a1b2"
}
}

(※読みやすく整形されています。)

感想

実装が多いので大変でした。
自分で実装したValueTupleのToStringメソッドが正しく実行できた時は嬉しかったです。
分からない事はこの記事のコメントで聞いてください。
間違えもこの記事のコメントで指摘してくれると、嬉しいです。
最後まで読んでくれてありがとうございます。

コメントを残す

WordPress.com で次のようなサイトをデザイン
始めてみよう