2

我有一个收藏

List<KeyValuePair<string, Details>> x

在哪里

public class Details
{ 
    private int x;
    private int y;

    public Details()
    {
        x = 0;
        y = 0;
    }
    ...
}

我在我的集​​合上使用 LINQ 来返回Details实例

x.Where(x => x.Key == aString).SingleOrNew().Value

其中.SingleOrNew定义为

public static T SingleOrNew<T>(this IEnumerable<T> query) where T : new()
{            
    try
    {
       return query.Single();
    }
    catch (InvalidOperationException)
    {
       return new T();
    }
}

因此,如果在满足子句 aKeyValuePair<string, Details>的列表中找不到a ,则返回。xWherenew KeyValuePair<string, Details>

但问题是new KeyValuePair<string, Details>包含一个空的详细信息值

当从子句中找不到匹配项时,.Where我想知道是否可以使用任何 LINQ(扩展)方法,它会返回一个new KeyValuePair<string, Details>like SingleOrNew,但是.Value/Details的部分KeyValuePair已使用默认的无Details参数构造函数初始化?所以它不为空!

4

2 回答 2

5

请改用此扩展方法:

    public static T SingleOrNew<T>(this IEnumerable<T> query, Func<T> createNew) 
    {
        try
        {
            return query.Single();
        }
        catch (InvalidOperationException)
        {
            return createNew();
        }
    }

您可能还想拥有它,因此您不需要 Where() 子句:

    public static T SingleOrNew<T>(this IEnumerable<T> query, Func<T,bool> predicate, Func<T> createNew) 
    {
        try
        {
            return query.Single(predicate);
        }
        catch (InvalidOperationException)
        {
            return createNew();
        }
    }

现在,您可以指定 T 的新实例应该是什么,而不是仅限于 T 的默认值,并具有公共无参数构造函数的约束。

于 2011-03-11T17:59:22.070 回答
1

其实我会这样做

public static T SingleOrNew<T>(this IEnumerable<T> query, Func<T> createNew)
{
    using (var enumerator = query.GetEnumerator())
    {
        if (enumerator.MoveNext() && !enumerator.MoveNext())
        {
            return enumerator.Current;
        }
        else
        {
            return createNew();
        }
    }
}

我不喜欢通过捕获异常来测试场景。

解释:

首先,我得到了 Enumerable 的 Enumerator。Enumerator 是一个具有MoveNext方法和Current属性的对象。该MoveNext方法将设置Current为枚举中的下一个值(如果有),如果有另一个值要移动,则返回 true,如果没有,则返回 false。此外,枚举器被包裹在 using 语句中,以确保在我们完成后将其处理掉。

So, after I get the enumerator I call MoveNext. If that returns false then the enumerable was empty and we will go straight to the else statement (bypassing the second MoveNext method because of short circuit evaluation) and return the result of createNew. If that first call returns true then we need to call MoveNext again to make sure there isn't a second value (because we want there to be a Single value). If the second call returns true then again it will go to the else statement and return the result of createNew. Now if the Enumerable does have only one value then the first call to MoveNext will return true and the second will return false and we will return the Current value that was set by the first call to MoveNext.

You can modify this to be a FirstOrNew by removing the second call to MoveNext.

于 2011-03-11T21:24:59.017 回答