#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
с правильными сигнатурами, и анализатор с радостью компилирует приведенный выше синтаксис запроса для использования этих методов расширения.