タプルを分解して渡す
Scala ではタプルをパターンマッチで分解してくれるので、タプルが非常に使いやすいです。
しかし、C# にはパターンマッチがないので、いちいち Item1 やら _1 やらでアクセスする必要があります。
例えば、
var strs = new[] { "aaa", "bbb", "ccc" }; var idxs = new[] { 0, 1, 2 }; var hoge = strs.Zip(idxs) .Select(str_i => string.Format("{1}:{0}", str_i._1, str_i._2));
こんな感じのコードは面倒ですし、読みやすいとはいえません (Zip やその結果のタプルは独自のものを使ってます)。
そこで、こんな感じのラッパーを書きました。
public static IEnumerable<TResult> Select<T1, T2, TResult>(this IEnumerable<Tpl<T1, T2>> self, Func<T1, T2, TResult> selector) { return self.Select(t => selector(t._1, t._2)); }
これを使うと、
var strs = new[] { "aaa", "bbb", "ccc" }; var idxs = new[] { 0, 1, 2 }; var hoge = strs.Zip(idxs) .Select((str, i) => string.Format("{0}:{1}", i, str));
このように、タプルの Item1 やら _1 やらを明示的に呼び出す必要がなくなり、より適切な名前をつけることが出来ます。
あとはいつも通りに T4 Template なのですが・・・C#3.5 では残念なことに Func が 4 引数版までしかありません。
なので、これも T4 Template で作ってしまいます。
<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #> <#@ output extension=".cs" encoding="UTF-8" #> <#@ Assembly Name="System.dll" #> <#@ Assembly Name="System.Core.dll" #> <#@ Assembly Name="System.Windows.Forms.dll" #> <#@ import namespace="System" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Diagnostics" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Collections" #> <#@ import namespace="System.Collections.Generic" #> <# for (int i = 5; i <= 16; i++) { var range = Enumerable.Range(1, i); var typeParam = string.Join(", ", range.Select(j => "T" + j).ToArray()); var funcParam = string.Join(", ", range.Select(j => string.Format("T{0} arg{0}", j)).ToArray()); var docComment4Func = @"/// <summary> /// {0}個のパラメーターを受け取って TResult パラメーターに指定された型の値を返すメソッドをカプセル化します。 /// </summary>"; WriteLine(docComment4Func, i); WriteLine("public delegate TResult Func<{0}, TResult>({1});", typeParam, funcParam); WriteLine(""); var docComment4Action = @"/// <summary> /// {0}個のパラメーターを受け取り、戻り値を持たないメソッドをカプセル化します。 /// </summary>"; WriteLine(docComment4Action, i); WriteLine("public delegate void Action<{0}>({1});", typeParam, funcParam); WriteLine(""); } #>
Func のついでに Action も作ってしまいました。
そして、本体。念のため 10 要素のタプルまで対応していますが、そのために生成されるコードは 4000 行をこえます。
10 要素分もいらないよ!って人は、n の数を変えてください。
<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #> <#@ output extension=".cs" encoding="UTF-8" #> <#@ Assembly Name="System.dll" #> <#@ Assembly Name="System.Core.dll" #> <#@ Assembly Name="System.Windows.Forms.dll" #> <#@ import namespace="System" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Diagnostics" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Collections" #> <#@ import namespace="System.Collections.Generic" #> <# int n = 10; #> using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CommonsSharp.Lang { partial class IEnumerableExtension { <# for (int i = 2; i <= n; i++) { var range = Enumerable.Range(1, i); var types = range.Select(j => "T" + j).ToArray(); var typeParam = string.Join(", ", types); var tupleExpand = string.Join(", ", range.Select(j => "t._" + j).ToArray()); #> public static IEnumerable<TResult> Select<<#= typeParam #>, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TResult> selector) { return self.Select(t => selector(<#= tupleExpand #>)); } public static IEnumerable<TResult> Select<<#= typeParam #>, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, int, TResult> selector) { return self.Select((t, i) => selector(<#= tupleExpand #>, i)); } public static IEnumerable<TResult> SelectMany<<#= typeParam #>, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, IEnumerable<TResult>> selector) { return self.SelectMany(t => selector(<#= tupleExpand #>)); } public static IEnumerable<TResult> SelectMany<<#= typeParam #>, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, int, IEnumerable<TResult>> selector) { return self.SelectMany((t, i) => selector(<#= tupleExpand #>, i)); } public static IEnumerable<TResult> SelectMany<<#= typeParam #>, TCollection, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, IEnumerable<TCollection>> collectionSelector, Func<<#= typeParam #>, TCollection, TResult> resultSelector) { return self.SelectMany(t => collectionSelector(<#= tupleExpand #>), (t, c) => resultSelector(<#= tupleExpand #>, c)); } public static TAccumulate Aggregate<<#= typeParam #>, TAccumulate>(this IEnumerable<Tpl<<#= typeParam #>>> self, TAccumulate seed, Func<TAccumulate, <#= typeParam #>, TAccumulate> func) { return self.Aggregate(seed, (acc, t) => func(acc, <#= tupleExpand #>)); } public static TResult Aggregate<<#= typeParam #>, TAccumulate, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, TAccumulate seed, Func<TAccumulate, <#= typeParam #>, TAccumulate> func, Func<TAccumulate, TResult> resultSelector) { return self.Aggregate(seed, (acc, t) => func(acc, <#= tupleExpand #>), resultSelector); } public static bool Any<<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, bool> predicate) { return self.Any(t => predicate(<#= tupleExpand #>)); } <# foreach (var methodName in new[] { "First", "Last", "Single" }) { #> public static Tpl<<#= typeParam #>> <#= methodName #><<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, bool> predicate) { return self.<#= methodName #>(t => predicate(<#= tupleExpand #>)); } public static Tpl<<#= typeParam #>> <#= methodName #>OrDefault<<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, bool> predicate) { return self.<#= methodName #>OrDefault(t => predicate(<#= tupleExpand #>)); } <# } #> <# foreach (var methodName in new[] { "Where", "SkipWhile", "TakeWhile" }) { #> public static IEnumerable<Tpl<<#= typeParam #>>> <#= methodName #><<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, bool> predicate) { return self.<#= methodName #>(t => predicate(<#= tupleExpand #>)); } public static IEnumerable<Tpl<<#= typeParam #>>> <#= methodName #><<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, int, bool> predicate) { return self.<#= methodName #>((t, i) => predicate(<#= tupleExpand #>, i)); } <# } #> public static IEnumerable<IGrouping<TKey, Tpl<<#= typeParam #>>>> GroupBy<<#= typeParam #>, TKey>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector) { return self.GroupBy(t => keySelector(<#= tupleExpand #>)); } public static IEnumerable<IGrouping<TKey, Tpl<<#= typeParam #>>>> GroupBy<<#= typeParam #>, TKey>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, IEqualityComparer<TKey> comparer) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), comparer); } public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<<#= typeParam #>, TKey, TElement>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>)); } public static IEnumerable<TResult> GroupBy<<#= typeParam #>, TKey, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<TKey, IEnumerable<Tpl<<#= typeParam #>>>, TResult> resultSelector) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), resultSelector); } public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<<#= typeParam #>, TKey, TElement>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector, IEqualityComparer<TKey> comparer) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>), comparer); } public static IEnumerable<TResult> GroupBy<<#= typeParam #>, TKey, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<TKey, IEnumerable<Tpl<<#= typeParam #>>>, TResult> resultSelector, IEqualityComparer<TKey> comparer) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), resultSelector, comparer); } public static IEnumerable<TResult> GroupBy<<#= typeParam #>, TKey, TElement, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>), resultSelector); } public static IEnumerable<TResult> GroupBy<<#= typeParam #>, TKey, TElement, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, IEqualityComparer<TKey> comparer) { return self.GroupBy(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>), resultSelector, comparer); } <# foreach (var methodName in new[] { "Average", "Max", "Min", "Sum" }) { foreach (var argRetType in new[] { "decimal", "double", "float", "int:double", "long:double", "decimal?", "double?", "float?", "int?:double?", "long?:double?" }) { var argType = argRetType.Split(':')[0]; var retType = argRetType.Split(':')[methodName == "Average" && argRetType.Contains(":") ? 1 : 0]; #> public static <#= retType #> <#= methodName #><<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, <#= argType #>> selector) { return self.<#= methodName #>(t => selector(<#= tupleExpand #>)); } <# } if (methodName == "Average" || methodName == "Sum") continue; #> public static TResult <#= methodName #><<#= typeParam #>, TResult>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TResult> selector) { return self.<#= methodName #>(t => selector(<#= tupleExpand #>)); } <# } #> public static int Count<<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, bool> predicate) { return self.Count(t => predicate(<#= tupleExpand #>)); } public static long LongCount<<#= typeParam #>>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, bool> predicate) { return self.LongCount(t => predicate(<#= tupleExpand #>)); } <# foreach (var methodName in new[] { "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending" }) { #> public static IOrderedEnumerable<Tpl<<#= typeParam #>>> <#= methodName #><<#= typeParam #>, TKey>(this <#= methodName.StartsWith("Then") ? "IOrderedEnumerable" : "IEnumerable" #><Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector) { return self.<#= methodName #>(t => keySelector(<#= tupleExpand #>)); } public static IOrderedEnumerable<Tpl<<#= typeParam #>>> <#= methodName #><<#= typeParam #>, TKey>(this <#= methodName.StartsWith("Then") ? "IOrderedEnumerable" : "IEnumerable" #><Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, IComparer<TKey> comparer) { return self.<#= methodName #>(t => keySelector(<#= tupleExpand #>), comparer); } <# } #> public static Dictionary<TKey, Tpl<<#= typeParam #>>> ToDictionary<<#= typeParam #>, TKey>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector) { return self.ToDictionary(t => keySelector(<#= tupleExpand #>)); } public static Dictionary<TKey, Tpl<<#= typeParam #>>> ToDictionary<<#= typeParam #>, TKey>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, IEqualityComparer<TKey> comparer) { return self.ToDictionary(t => keySelector(<#= tupleExpand #>), comparer); } public static Dictionary<TKey, TElement> ToDictionary<<#= typeParam #>, TKey, TElement>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector) { return self.ToDictionary(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>)); } public static Dictionary<TKey, TElement> ToDictionary<<#= typeParam #>, TKey, TElement>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector, IEqualityComparer<TKey> comparer) { return self.ToDictionary(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>), comparer); } public static ILookup<TKey, Tpl<<#= typeParam #>>> ToLookup<<#= typeParam #>, TKey>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector) { return self.ToLookup(t => keySelector(<#= tupleExpand #>)); } public static ILookup<TKey, Tpl<<#= typeParam #>>> ToLookup<<#= typeParam #>, TKey>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, IEqualityComparer<TKey> comparer) { return self.ToLookup(t => keySelector(<#= tupleExpand #>), comparer); } public static ILookup<TKey, TElement> ToLookup<<#= typeParam #>, TKey, TElement>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector) { return self.ToLookup(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>)); } public static ILookup<TKey, TElement> ToLookup<<#= typeParam #>, TKey, TElement>(this IEnumerable<Tpl<<#= typeParam #>>> self, Func<<#= typeParam #>, TKey> keySelector, Func<<#= typeParam #>, TElement> elementSelector, IEqualityComparer<TKey> comparer) { return self.ToLookup(t => keySelector(<#= tupleExpand #>), t => elementSelector(<#= tupleExpand #>), comparer); } <# } #> } }
面倒なので Join 系は実装していません。
あと微妙に規則性があるので、もうちょっと短く出来そうです。