Доступны ли опции для «поддельного» ключевого слова синтаксиса linq в C #? - PullRequest
3 голосов
/ 08 января 2012

Хотя есть несколько случаев, когда я что-то напишу, используя цепочки методов (особенно, если это всего лишь один или два метода, например, foo.Where (..). ToArray ()), во многих случаях я предпочитаю LINQ. вместо этого синтаксис понимания запроса («выражения запроса» в спецификации), что-то вроде:

var query =
    from filePath in Directory.GetFiles(directoryPath)
    let fileName = Path.GetFileName(filePath)
    let baseFileName = fileName.Split(' ', '_').First()
    group filePath by baseFileName into fileGroup
    select new
    {
        BaseFileName = fileGroup.Key,
        Count = fileGroup.Count(),
    };

В некоторой довольно значительной части этого я должен взять полученный IEnumerable и нетерпеливо загрузить его в структуру данных (массив, список, что угодно). Обычно это означает либо:

  1. добавление еще одной локальной переменной, такой как var queryResult = query.ToArray (); или

  2. завершение запроса паренями и тегами в ToArray (или ToList или что-то еще).

var query = (
    from filePath in Directory.GetFiles(directoryPath)
    let fileName = Path.GetFileName(filePath)
    let baseFileName = fileName.Split(' ', '_').First()
    group filePath by baseFileName into fileGroup
    select new
    {
        BaseFileName = fileGroup.Key,
        Count = fileGroup.Count(),
    }
).ToArray();

Я пытаюсь выяснить, какие варианты другие либо 1) уже используют, либо 2) могли бы посчитать возможным добавить некоторые дополнительные «контекстные ключевые слова» - просто вещи, которые могли бы преобразовываться в методы расширения так же, как существующие некоторые делают, как если бы ключевые слова LINQ были «изначально» расширяемыми:)

Я понимаю, что, скорее всего, это будет означать либо какую-то предварительную обработку (не уверен, что там в этой области для C #), либо изменение используемого компилятора на что-то вроде Nemerle (я думаю, что это будет будет вариант, но не совсем уверен?). Я пока не знаю достаточно о том, что Рослин делает / будет поддерживать, поэтому, если кто-то знает, может ли он позволить кому-то «расширять» C # таким образом, пожалуйста, присоединяйтесь!

Те, которые я, вероятно, буду использовать чаще всего (хотя я уверен, что есть много других, но просто чтобы донести идею / на что я надеюсь):

ascount - преобразуется в Count ()

int zFileCount =
    from filePath in Directory.GetFiles(directoryPath)
    where filePath.StartsWith("z")
    select filePath ascount;

Это "преобразовало бы" (не важно, какой путь, если конечный результат) в:

int zFileCount = (
    from filePath in Directory.GetFiles(directoryPath)
    where filePath.StartsWith("z")
    select filePath
).Count();
* +1034 * Аналогично:
  • asarray - преобразуется в ToArray ()
  • aslist - преобразуется в ToList ()

(очевидно, вы могли бы продолжать использовать First (), Single (), Any () и т. Д., Но пытаться контролировать область вопроса:)

Меня интересуют только методы расширения, которым не нужно передавать параметры. Я не ищу попытки сделать что-то подобное с (например) ToDictionary или ToLookup. :)

Итак, в итоге:

  • хотите добавить 'ascount', 'aslist' и 'asarray' в выражения запроса linq
  • не знаю, было ли это уже решено
  • не знаю, является ли Nemerle хорошим выбором для этого
  • не знаю, поддержит ли история Рослин этот вид использования

Ответы [ 3 ]

13 голосов
/ 08 января 2012

Не ответ на ваш вопрос, а некоторые размышления о вашем дизайне.Мы решительно решили добавить такую ​​возможность в C # 4, но сократили ее, потому что у нас не было времени и ресурсов.

Проблема с синтаксисом понимания запросов, как вы заметили, заключается в том, что смешивать синтаксис «беглый» и «понимание» некрасиво.Вы хотите знать, сколько разных фамилий у ваших клиентов в Лондоне, и в итоге вы пишете эту уродливую вещь с круглыми скобками:

d = (from c in customers 
     where c.City == "London" 
     select c.LastName)
    .Distinct()
    .Count();

Гадость.

Мы рассмотрели возможность добавления нового контекстного ключевого слова всинтаксис понимания.Скажем ради аргумента, что ключевое слово "с".Вы могли бы тогда сказать:

d = from c in customers 
    where c.City == "London" 
    select c.LastName
    with Distinct() 
    with Count();

, и переписчик понимания запросов переписал бы это в соответствующий свободный синтаксис.

Мне действительно нравится эта функция, но она не сделала сокращение для C # 4 или5. Было бы неплохо включить его в гипотетическую будущую версию языка.

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

3 голосов
/ 08 января 2012

По идее, вы можете написать свой собственный поставщик запросов, который обернет версию в System.Linq и затем вызовет ToArray в его методе Select. Тогда у вас будет просто using YourNamespace; вместо using System.Linq.

Roslyn не позволяет вам расширять синтаксис C #, но вы можете написать SyntaxRewriter, который изменяет семантику программы C # в качестве шага перестройки.

2 голосов
/ 08 января 2012

Как говорили другие, Рослин - это не то, о чем вы, вероятно, думаете.Его нельзя использовать для расширения C #.

Весь следующий код следует считать более мозговым штурмом и меньшим количеством рекомендаций.Это меняет поведение LINQ непредсказуемым образом, и вам следует очень серьезно подумать, прежде чем использовать что-либо подобное.

Один из способов решить эту проблему - изменить выражение select следующим образом:

int count = from i in Enumerable.Range(0, 10)
            where i % 2 == 0
            select new Count();

Реализация может выглядеть следующим образом:

public  class Count
{}

public static class LinqExtensions
{
    public static int Select<T>(
        this IEnumerable<T> source, Func<T, Count> selector)
    {
        return source.Count();
    }
}

Если вы поместите в select что-либо, что не Count, оно будет вести себя как обычно.

Делать что-то подобноедля массивов потребовалось бы больше работы, так как вам нужно select, чтобы указать, что вы хотите массив и селектор элементов, которые вы хотите там, но это выполнимо.Или вы можете использовать два select s: один выбирает элемент, а другой говорит, что вы хотите массив.

Другой вариант (аналогичный предложению Кевина) будет иметь такой метод расширения, как AsCount(), который вы можете использоватьиспользуйте вот так:

int count = from i in Enumerable.Range(0, 10).AsCount()
            where i % 2 == 0
            select i;

Вы можете реализовать это так:

public static class LinqExtensions
{
    public static Countable<T> AsCount<T>(this IEnumerable<T> source)
    {
        return new Countable<T>(source);
    }
}

public class Countable<T>
{
    private readonly IEnumerable<T> m_source;

    public Countable(IEnumerable<T> source)
    {
        m_source = source;
    }

    public Countable<T> Where(Func<T, bool> predicate)
    {
        return new Countable<T>(m_source.Where(predicate));
    }

    public Countable<TResult> Select<TResult>(Func<T, TResult> selector)
    {
        return new Countable<TResult>(m_source.Select(selector));
    }

    // other LINQ methods

    public static implicit operator int(Countable<T> countable)
    {
        return countable.m_source.Count();
    }
}

Я не уверен, что мне нравится это так.Особенно неявный актерский состав кажется неправильным, но я думаю, что другого пути нет.

...