3

問題

Xが実装FirstOrEmptyする場所を実装する方法を探しています。基本的に、述語が何にも一致しない場合は、 を返します。source パラメーターを or に制約したくありません。これは、実装するもの(例: )を渡したい場合があるためです。IEnumerable<X>IEnumerable<T>Enumerable<T>.EmptyIEnumerable<IEnumerable<X>>IEnumerable<X>IEnumerable<IGrouping<bool, X>>

使用例

IEnumerable<T> myCollection;
var groupCheck = myCollection.GroupBy(t => t.SomeProp == 23);

var badGroup = groupCheck.FirstOrEmpty(t => !t.Key);
var goodGroup = groupCheck.FirstOrEmpty(t => t.Key);

foreach(T x in badGroup) { ... }
foreach(T x in goodGroup) { ... }

古い方法:

IEnumerable<T> myCollection = ...;
var groupCheck = myCollection.GroupBy(t => t.SomePropOnClassT == 23);

var badGroup = (groupCheck.FirstOrDefault(t => !t.Key) ?? Enumerable<T>.Empty);
var goodGroup = (groupCheck.FirstOrDefault(t => t.Key) ?? Enumerable<T>.Empty);

foreach(T x in badGroup) { ... }
foreach(T x in goodGroup) { ... }

試み

試行 1:

public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
    TSource tmp = source.FirstOrDefault(predicate);
    if(tmp != null) {
        foreach(TResult x in tmp)
        {
            yield return x; 
        } 
    }
}

試行 2:

public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
    TSource tmp = source.FirstOrDefault(predicate);
    return tmp == null ? Enumerable.Empty<TResult>() : tmp;
}
4

3 に答える 3

4

あなた自身の解決策:

public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
    )
    where TSource : class, IEnumerable<TResult>
{
    return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}

実際に働いています。class制約を追加しただけです。これは「TSource参照型でなければならない」ことを意味し、インタフェースはclass制約付きで OK です。この理由は、一部の構造体でdefault(TSource)はない可能性nullがあるためですTSource。次のように呼び出すことができます。

var badGroup = groupCheck.FirstOrEmpty<IGrouping<bool, T>, T>(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty<IGrouping<bool, T>, T>(g => g.Key);

残念ながら、コンパイラは型パラメータ自体を理解するほどスマートではないため、上記のように山かっこでそれらを指定する必要があります<..., ...>。私はその解決策を見つけていません。

これを で常に使用する場合IGrouping<,>は、次のやや一般的ではない方法を使用することをお勧めします。

public static IEnumerable<TResult> FirstOrEmpty<TKey, TResult>(
    this IEnumerable<IGrouping<TKey, TResult>> source,
    Func<IGrouping<TKey, TResult>, bool> predicate
    )
{
    return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}

今回は次のようになります。

var badGroup = groupCheck.FirstOrEmpty<bool, T>(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty<bool, T>(g => g.Key);

良いニュースは、このアプローチを使用すると、コンパイラ型引数を推論することです。

var badGroup = groupCheck.FirstOrEmpty(g => !g.Key);
var goodGroup = groupCheck.FirstOrEmpty(g => g.Key);

動作します。

于 2012-12-10T16:51:19.177 に答える
2

おそらく試行 2 のリファクタリングされたバージョンを使用します。

public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate) where TSource : class, IEnumerable<TResult>
{
    return source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}

編集:この回答が反対票を投じられた理由を知りたいです。

于 2012-12-06T19:47:33.147 に答える
1

@phoog と同じ結果が得られましたが、コンパイラに型パラメータを推測させるのに苦労しています。

public static IEnumerable<TResult> FirstOrEmpty<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate) where TSource : IEnumerable<TResult>
{
    return (IEnumerable<TResult>)source.FirstOrDefault(predicate) ?? Enumerable.Empty<TResult>();
}

私が思いついた最善の方法は、それらを明示的に述べることです:

var badGroup = groupCheck.FirstOrEmpty<IGrouping<bool,int>,int>(t => !t.Key);
var goodGroup = groupCheck.FirstOrEmpty<IGrouping<bool,int>,int>(t => t.Key);
于 2012-12-06T19:36:19.293 に答える