LINQ中的SingleOrNew()方法而不是SingleOrDefault()怎么样?

时间:2009-02-18 16:11:24

标签: c# linq

SingleOrDefault()方法很棒,因为如果你调用它的集合是空的,它不会抛出异常。但是,有时我想要的是获取某种类型的新对象(如果不存在)。例如,如果我能做到以下几点就会很棒:

var client = db.Clients
    .Where(c => c.Name == "Some Client")
    .SingleOrNew<Client>();

这样我就不必检查它是否为null,如果它是创建一个新的,我总是知道我的client对象将是我可以立即使用的东西。

有什么想法吗?

6 个答案:

答案 0 :(得分:21)

真的,您只想在这里使用null coalescing运算符。例如:

var client = db.Clients
    .Where(c => c.Name == "Some Client")
    .SingleOrDefault() ?? new Client();

这将返回SingleOrDefault返回的任何内容,但如果SingleOrDefault返回null,则表达式将返回新的Client()。

编辑:正如John Skeet指出的那样,这个解决方案没有区分没有匹配和找到null元素的情况,但显然这不一定是问题在许多情况下。另一种方法是创建一个扩展方法,如下所示。

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

我想说这可能是一般案例中最优雅的解决方案。

答案 1 :(得分:9)

如果要完成的任务是覆盖默认值(并返回一个新对象),则可以在调用DefaultIfEmpty()之前使用SingleOrDefault()方法执行此操作。类似的东西:

var client = db.Clients
    .Where(c => c.Name == name)
    .DefaultIfEmpty(new Client { Name = name })
    .SingleOrDefault();

答案 2 :(得分:4)

会这样吗? 编辑原来那个??不适用于泛型类型。

public static class IEnumerableExtensions
{
    public static T SingleOrNew<T>( this IEnumerable<T> query ) where T : new()
    {
        var value = query.SingleOrDefault();
        if (value == null)
        {
            value = new T();
        }
        return value;
    }
}

答案 3 :(得分:2)

听起来可以做到,是的。不能说我记得曾经处于我自己使用它的情况,但它很容易实现。像这样:

public static T SingleOrNew<T>(this IEnumerable<T> source) where T : new()
{
    if (source == null)
    {
        throw new ArgumentNullException("source"); 
    }
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            return new T();
        }
        T first = iterator.Current;
        if (iterator.MoveNext())
        {
            throw new InvalidOperationException();
        }
        return first;
    }
}

我会将其添加到要包含在MoreLinq ...

中的运算符列表中

另一种更通用的方法是提供一个只在必要时才进行评估的代表(诚然,我需要一个更好的名字):

public static T SingleOrSpecifiedDefault<T>(this IEnumerable<T> source,
     Func<T> defaultSelector)
{
    if (source == null)
    {
        throw new ArgumentNullException("source"); 
    }
    if (defaultSelector == null)
    {
        throw new ArgumentNullException("defaultSelector"); 
    }
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            return defaultSelector();
        }
        T first = iterator.Current;
        if (iterator.MoveNext())
        {
            throw new InvalidOperationException();
        }
        return first;
    }
}

答案 4 :(得分:1)

为什么不通过Extension方法添加呢?听起来很方便。

答案 5 :(得分:0)

var theClient =
    Repository<Client>
        .Entities()
        .Where(t => t.ShouldBeHere())
        .SingleOrDefault()
        ?? new Client { Name = "Howdy!" }
    ;