Есть ли разница между этими двумя операторами в Linq?

#c# #linq

#c# #linq

Вопрос:

Есть ли разница между этими двумя операторами. Я выполнил тест, и он не прошел один из тестов при отложенном выполнении. В чем будет разница?

 items.Where(w => w.Length >= length).Select(w => w.Substring(0, length)).Distinct();
  

и

 (from a in items
                where a.Length >= 3
                select a.Substring(0, length)).Distinct();
  

Они читают мне то же самое, и я полагаю, что делают точно то же самое?

Комментарии:

1. Опечатка? w => w.Length >= length и a.Length >= 3 разница очевидна, если length != 3

2. Как говорит @DmitryBychenko, эти два отличаются прямо сейчас, потому что в первом примере у вас есть w.Length >= length , тогда как в другом у вас есть a.Length >= 3 . Не могли бы вы, пожалуйста, уточнить, предполагали ли вы наличие этой разницы?

3. В конце концов, проблема была не в 3, а в том, что возникла проблема с тестом. Я пробовал оба способа, и нижний из них выдал проблему.

Ответ №1:

Я предполагаю, что 3 vs length — это опечатка.

Пройдя через это, хитрость вот в чем: попробуйте; хорошим инструментом здесь является sharplab.io — вот два примера, и мы можем видеть — после компиляции и декомпиляции — что M и N примерно одно и то же:

 public void M()
{
    IEnumerable<string> enumerable = Enumerable.Distinct(Enumerable.Select(Enumerable.Where(items, new Func<string, bool>(<M>b__2_0)), new Func<string, string>(<M>b__2_1)));
}

public void N()
{
    IEnumerable<string> enumerable = Enumerable.Distinct(Enumerable.Select(Enumerable.Where(items, new Func<string, bool>(<N>b__3_0)), new Func<string, string>(<N>b__3_1)));
}
  

Они используют разные методы, сгенерированные компилятором, но реализации одинаковы:

 [CompilerGenerated]
private bool <M>b__2_0(string w)
{
    return w.Length >= length;
}

[CompilerGenerated]
private string <M>b__2_1(string w)
{
    return w.Substring(0, length);
}

[CompilerGenerated]
private bool <N>b__3_0(string a)
{
    return a.Length >= length;
}

[CompilerGenerated]
private string <N>b__3_1(string a)
{
    return a.Substring(0, length);
}
  

Итак: мы можем заключить, что да, они одинаковы.

Комментарии:

1. Я думаю, проблема была в том, что я пропустил >= 3, и именно поэтому у меня возникла ошибка

Ответ №2:

Предполагая, что вы хотели написать where a.Length >= length , тогда нет. Компилятор всегда преобразует синтаксис запроса в эквивалентный синтаксис, используя методы расширения.

Вы можете видеть это с помощью SharpLab — оба они компилируются во что-то, что выглядит как:

 [CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
    public int length;

    internal bool <Query>b__0(string a)
    {
        return a.Length >= length;
    }

    internal string <Query>b__1(string a)
    {
        return a.Substring(0, length);
    }
}

public void Query(IEnumerable<string> items, int length)
{
    <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
    <>c__DisplayClass0_.length = length;
    Enumerable.Distinct(Enumerable.Select(Enumerable.Where(items, new Func<string, bool>(<>c__DisplayClass0_.<Query>b__0)), new Func<string, string>(<>c__DisplayClass0_.<Query>b__1)));
}
  

Вы можете использовать эту эквивалентность между синтаксисом запроса и методами расширения. Компилятор позволяет использовать синтаксис запроса для типов, у которых есть методы расширения с определенными правильными именами, независимо от того, определены ли они на IEnumerable<T> , IQueryable<T> или где-либо еще.

Это пример «утиного ввода» в компиляторе, когда компилятор не заботится о конкретных типах интерфейсов, а только о том, что определенный метод с правильным именем и сигнатурой вообще существует.

Например, это компилирует:

 public class C {
    public void Query(IThing<string> thing) {
        var y = from x in thing
            select x;
    }
}

public interface IThing<T>
{
    T Foo { get; }   
}

public static class ThingExtensions
{
    public static IThing<T> Select<T>(this IThing<T> thing, Func<IThing<T>, IThing<T>> selector)
    {
        return selector(thing);
    }
}
  

Это используется, например, в библиотеке комбинатора синтаксического анализа Sprache, которая позволяет вам написать анализатор, подобный:

 Parser<string> identifier =
    from leading in Parse.WhiteSpace.Many()
    from first in Parse.Letter.Once()
    from rest in Parse.LetterOrDigit.Many()
    from trailing in Parse.WhiteSpace.Many()
    select new string(first.Concat(rest).ToArray());
  

Здесь нет IEnumerable<T> or IQueryable<T> — библиотека просто определяет методы расширения Select и SelectMany с правильными сигнатурами, и анализатор с радостью компилирует приведенный выше синтаксис запроса для использования этих методов расширения.