简体   繁体   English

使用Enumerable.Empty的意外行为 <string> ()

[英]Unexpected behavior using Enumerable.Empty<string>()

I would expect Enumerable.Empty<string>() to return an empty array of strings. 我希望Enumerable.Empty<string>()返回一个空字符串数组。 Instead, it appears to return an array with a single null value. 相反,它似乎返回一个具有单个null值的数组。 This breaks other LINQ operators like DefaultIfEmpty , since the enumerable is not, in fact, empty. 这会破坏其他LINQ运算符(如DefaultIfEmpty ,因为可枚举实际上并不为空。 This doesn't seem to be documented anywhere, so I'm wondering if I'm missing something (99% probability). 这似乎没有记录在任何地方,所以我想知道我是否缺少某些东西(99%的概率)。

GameObject Class GameObject类

 public GameObject(string id,IEnumerable<string> keywords) {
        if (String.IsNullOrWhiteSpace(id)) {
            throw new ArgumentException("invalid", "id");
        }
        if (keywords==null) {
            throw new ArgumentException("invalid", "keywords");
        }
        if (keywords.DefaultIfEmpty() == null) { //This line doesn't work correctly.
            throw new ArgumentException("invalid", "keywords");
        }
        if (keywords.Any(kw => String.IsNullOrWhiteSpace(kw))) {
            throw new ArgumentException("invalid", "keywords");
        }

        _id = id;
        _keywords = new HashSet<string>(keywords);
    }

Test 测试

    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void EmptyKeywords() {
        GameObject test = new GameObject("test",System.Linq.Enumerable.Empty<string>());
    }

It looks like you expect this condition: 您似乎期望这种情况:

keywords.DefaultIfEmpty() == null

to evaluate to true . 评估为true However DefaultIfEmpty returns a singleton sequence containing the default for the element type ( string in this case) if the source sequence is empty. 但是,如果源序列为空,则DefaultIfEmpty返回一个单例序列,其中包含元素类型的默认值(在这种情况下为string )。 Therefore it will return a sequence containing null . 因此它将返回一个包含null的序列。 This is not itself null however so the condition returns false . 但是它本身并不为null因此条件返回false

You are misinterpreting the implementation of DefaultIfEmpty , here is it's implementation from the reference source . 您误解了DefaultIfEmpty的实现,这是来自参考源的实现。

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source) {
    return DefaultIfEmpty(source, default(TSource));
}

public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source, TSource defaultValue) {
    if (source == null) throw Error.ArgumentNull("source");
    return DefaultIfEmptyIterator<TSource>(source, defaultValue);
}

static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue) {
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        if (e.MoveNext()) {
            do {
                yield return e.Current;
            } while (e.MoveNext());
        }
        else {
            yield return defaultValue;
        }
    }
}

So what it does is if a IEnumerable<T> is not empty it simply returns the IEnumerable<T> , if the IEnumerable<T> is empty it returns new a IEnumerable<T> with one object in it with the value default(T) . 所以它的作用是,如果IEnumerable<T>不为空,则仅返回IEnumerable<T> ;如果IEnumerable<T>为空,则返回新的IEnumerable<T> ,其中一个对象的值为default(T) It will never return null which is what your test is testing for. 它永远不会返回null ,这就是您的测试所测试的。 If you wanted to test this you would need to do 如果您想测试一下,您需要做

if(keywords.DefaultIfEmpty().First() == null)

However this is going to cause the IEnumerable<string> to be evaluated multiple times. 但是,这将导致IEnumerable<string>被多次评估。 I would drop the LINQ and just do like the LINQ method does and do it the long way (this also gets rid of the extra evaluation you had inside new HashSet<string>(keywords) ). 我将删除LINQ并像LINQ方法一样做,并且做得很长(这也消除了您在new HashSet<string>(keywords)拥有的额外评估)。

 public GameObject(string id,IEnumerable<string> keywords) 
 {
    if (String.IsNullOrWhiteSpace(id)) {
        throw new ArgumentException("invalid", "id");
    }
    if (keywords==null) {
        throw new ArgumentException("invalid", "keywords");
    }

    _keywords = new HashSet<string>();

    using (var enumerator = keywords.GetEnumerator())
    {
        if (e.MoveNext())
        {
            do 
            {
                if(e.Current == null)
                    throw new ArgumentException("invalid", "keywords");

                _keywords.Add(e.Current);

            } while (e.MoveNext());
        }
        else 
        {
            throw new ArgumentException("invalid", "keywords");
        }
    }

    _id = id;
}

This makes it so you only loop once over the IEnumerable<string> . 这样可以使您仅在IEnumerable<string>循环一次。

Does this solve your problem? 这样可以解决您的问题吗?

 public GameObject(string id, IEnumerable<string> keywords) {
    if (String.IsNullOrWhiteSpace(id)) {
        throw new ArgumentException("invalid", "id");
    }
    if (keywords == null || !keywords.Any()
            || keywords.Any(k => String.IsNullOrWhiteSpace(k))) {
        throw new ArgumentException("invalid", "keywords");
    }

    _id = id;
    _keywords = new HashSet<string>(keywords);
}

*Improved the code with suggestions from @ScottChamberlain & @ginkner *通过@ScottChamberlain和@ginkner的建议改进了代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Enumerable.Empty 列出 - Enumerable.Empty to List 哪个更好,Enumerable.Empty <T> 还是新的[0]? - Which is better, Enumerable.Empty<T> or new[0]? Enumerable.Empty<t> () 等效于 IQueryable</t> - Enumerable.Empty<T>() equivalent for IQueryable Enumerable.Empty 需要显式转换<T> - Explicit cast required for Enumerable.Empty<T> 为什么Enumerable.Empty()返回一个空数组? - Why does Enumerable.Empty() return an empty array? Null合并运算符IList,Array,Enumerable.Empty in foreach - Null coalescing operator IList, Array, Enumerable.Empty in foreach 返回 Enumerable.Empty<t> ().AsQueryable() 一个坏主意?</t> - Returning Enumerable.Empty<T>().AsQueryable() a bad idea? 将 Enumerable.Empty&lt;&gt;() 转换为另一个实现 IEnumerable 的 class 会返回 null - Casting an Enumerable.Empty<>() into another class that implements IEnumerable returns null 使用 Enumerable.Empty 是否更好<T> () 而不是 new List<T> () 初始化一个 IEnumerable<T> ? - Is it better to use Enumerable.Empty<T>() as opposed to new List<T>() to initialize an IEnumerable<T>? Enumerable.Empty<t> ().AsQueryable(); 此方法支持 LINQ 到实体基础设施,不打算直接从您的代码中使用</t> - Enumerable.Empty<T>().AsQueryable(); This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM