2012-01-07 1 views
3

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

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. упаковка запроса с помощью parens и пометки на 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 (I думаю, что это будет вариант, но не совсем уверен?). Я не знаю достаточно о том, что Roslyn делает/будет поддерживать, поэтому, если кто-то знает, может ли он позволить кому-то «продлить» C#, как это, пожалуйста, звоните!

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

ascount - прообразы графу()

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(); 

Аналогично:

  • asarray - превращается в ToArray()
  • aslist - превращается в ToList()

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

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

Итак, в заключение:

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

Roslyn делает * не * позволяет расширить язык C#! См. Блог Эрика Липперта (http://blogs.msdn.com/b/ericlippert/archive/2011/10/19/the-roslyn-preview-is-now-available.aspx). –

+1

@Cicada: Было бы более точно сказать, что добавление языковых расширений не является * целью * Roslyn; целью Roslyn является предоставление пользователям лексических, синтаксических и семантических аналитических механизмов компилятора. Если они найдут какой-то сумасшедший способ использовать эти двигатели для расширения языка, это хорошо для них. Но мы не разрабатываем Roslyn как механизм добавления языковых расширений; мы разрабатываем его как механизм для пользователей, чтобы потреблять те же механизмы анализа, которые потребляют компилятор и команды IDE. –

+0

Лично я отделил бы ваш запрос от подсчета: 'var query = from [...];' 'int count = query.Count();' Я думаю, что это выглядит не так уродливо, как обертывание всего запроса, и, кроме того, вы может повторно использовать запрос, если вам это нужно для других вещей, помимо подсчета в будущем. – ordag

ответ

12

Не ответ на ваш вопрос, а несколько размышлений о вашем дизайне. Мы решительно решили добавить такую ​​возможность к 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(); 

и понимание запросов ReWriter бы переписать, что в соответствующий беглый синтаксис.

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

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

+0

Спасибо за информацию! Что касается потенциального использования Roslyn для добавления поддержки такого ключевого слова, я понимаю, что это не «поддерживаемая» операция как есть, но одна вещь, о которой вы писали, заключалась в том, что структуры данных Roslyn необходимы для поддержки концепций разбитого кода , Это заставило меня задаться вопросом, может ли Рослин относиться к ключевому слову «asarray» в конце как к некоему сломанному коду, который потребительский код (независимо от того, что вызывает Roslyn) мог бы затем найти и «исправить» код, чтобы он выполнял компиляцию, «переписать» выражение в (...). ToArray(), то пусть Roslyn продолжит, как обычно? –

+1

@JamesManning: парсер Roslyn сохранит сломанный код в грамматическом анализе, который он вам дает, но мы не даем никаких гарантий относительно пригодности этого анализа для любой конкретной цели, которую вы можете иметь для этого. Например, при столкновении с «var q = from a in b select c garbage»; синтаксический анализатор был бы полностью в пределах своих прав, чтобы сказать, что точка с запятой отсутствует после c, вставьте виртуальную точку с запятой, а затем начните анализ «мусора»; как новое утверждение, а затем дать вам дополнительную ошибку, что «мусор»; не является заявлением о юридическом выражении. –

+4

@JamesManning: Сломанный анализатор кода может выбрать и другую интерпретацию этого кода; он мог бы вместо этого решить, что недостающее - это «в», которое может юридически следовать «выбрать». Анализ сломанного кода - это больше искусства, чем науки, и у нас много дурацких эвристик, которые могут дать неожиданные результаты. –

3

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

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

+0

Вызов 'ToArray()' в 'Select()' не будет работать вообще, потому что 'Select()' не вызывается, если конечная проекция является личным. – svick

2

Как говорили другие, Рослин - это не то, что вы, вероятно, думаете. Он не может использоваться для расширения 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(); 
    } 
} 

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

Выполнение чего-то подобного для массивов потребует больше работы, так как вам нужно 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(); 
    } 
} 

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