#c# #linq
#c# #linq
Вопрос:
У меня есть фрагменты кода:
Метод заключается в следующем:
public IEnumerable<c> Test(){
var collection = new a();
IEnumerable<c> bs = collection.Where(item => item.Id > 10).Select(item => item);
return from item in collection
where item.Id > 10
select item;
}
(первые две строки компилируются нормально)
Он не компилируется и завершается с ошибкой:
Невозможно неявно преобразовать тип
System.Collections.Generic.IEnumerable<Stackoverflow.b>
вSystem.Collections.Generic.IEnumerable<Stackoverflow.c>
однако приведенный ниже код компилируется просто отлично:
return from item in collection
where item.Id > 10
select new b(null);
поскольку тип выражения в предложении select одинаков, я бы ожидал, что он скомпилируется в обоих случаях или завершится неудачей в обоих.
если я использую синтаксис метода вместо понимания запроса и записи:
collection.Where(item => item.Id > 10).Select(item => item)
это тоже компилируется
мой вопрос: «Что-то не так с юниверсом» (это ошибка компилятора) или я что-то упускаю?
Редактировать
У меня нет using System.Linq
, поэтому обычный select и where методы не используются, только те, которые указаны ниже кода, являются частью академического упражнения, чтобы попытаться расширить разрешение перегрузки LINQ на основе шаблонов. Подписи намеренно странные / неожиданные, и где включает проекцию, хотя я считаю, что это, как правило, было бы довольно плохой идеей.
Редактировать
Определения рассматриваемых классов и метод расширения select
public static class Enumerable {
public static IEnumerable<c> Select(this IEnumerable<b> self, Func<b,b> selector){
return null;
}
}
public class a{
public IEnumerable<b> Where(Predicate<a> predicate){
return null;
}
public int Id { get; set; }
}
public class b{
}
public class c{
}
Комментарии:
1. Какова сигнатура метода, в котором находится этот код? Каков тип переменной, которой вы ее присваиваете?
2. Я понимаю соблазн приправить вещи юмором и ссылками на блоги, которые вам нравятся, но ваш вопрос будет проверен людьми, которые могут ответить на него на основе его названия, а затем в течение многих лет он будет проверяться по названию снова людьми, у которых может быть та же проблема, или совершенно другая.несвязанная проблема. Вы не помогаете никому из этих людей. PS: маловероятно, что вы обнаружили ошибку компилятора, особенно если предположительно ошибочное поведение заключается в выдаче сообщения об ошибке. Большинство ошибок компилятора заключаются в том, что они автоматически выдают неправильный код.
3. Каково определение
collection
?4. @Oded Я опубликовал определения. Я не считаю это важным для более общего вопроса «когда типы всех выражений одинаковы, почему он терпит неудачу в экземпляре, а не в другом», однако 🙂
5. Вы действительно верили, что это может быть ошибка компилятора?
Ответ №1:
Это все о переводе выражений запроса. Возможно, вы захотите взглянуть на спецификацию языка C # 4.0 7.16.2 (или 7.15.2 для C # 3.0) для получения более подробной информации, но вкратце, выражения, такие как:
from x in c
where f(x)
select x
Переводятся только в
c.Where(x => f(x))
итак, ваш перечисляемый.Select не будет вызываться, и в результате вы получите IEnumerable<b> вместо IEnumerable<c> . Именно то, на что жалуется компилятор.
Комментарии:
1. раздел, в котором я его нашел, был 7.15.2, хотя
2. да, это было 7.15.2 в C # 3.0. Я отредактировал свой ответ, чтобы упростить поиск этой части спецификации.
3. Интересный выбор, чтобы всегда удалять выбор. Довольно странно, я думаю, поскольку, если где не указано, выбор фактически выполняется (7.15.2.3), что в этом случае приводит к другому типу возвращаемого значения в двух случаях
4. @RuneFS: было бы излишне вызывать identity Select в обычных случаях, тем не менее «Важно, однако, чтобы результат выражения запроса никогда не был самим исходным объектом», вот почему он вызывается, когда нет «where». Я бы сказал, что было бы лучше избегать таких методов выбора, которые у вас есть, и вместо этого использовать отдельный метод расширения, например ConvertToC в дополнение к обычному Select .
5. Я полностью согласен, что с точки зрения кодирования представленный мной код имеет мало смысла. Однако это произошло как часть академического упражнения, намеренно пытающегося написать странный / удивительный linq. Пытаюсь понять, насколько перегружен метод на основе шаблонов. разрешение может быть изменено. Лично мне жаль, что элемент from в x, где предварительно выбранный элемент, не должен совпадать с x.Где(..).Select(..) или где true select — это не то же самое, что просто select . Я считаю, что это нарушает «наименьшее удивление»
Ответ №2:
Как указано в сообщении об ошибке.
Вы не возвращаете то же значение, что и сигнатура метода.
Если вы прочитали сообщение об ошибке, оно означает, что оно ожидает C, но получает B
Комментарии:
1. Затем объясните, почему один и тот же код в синтаксисе метода действительно возвращает тип сигнатуры метода. Метод selct действительно возвращает C. Если вы были правы, то почему …select new b(null) работает?
2. Прочитайте ответ Константина Ознобихина.
3. И я (косвенно) спрашивал, верно ли сообщение об ошибке, тогда в чем разница между выражениями. Что ваш ответ не объясняет
Ответ №3:
Попробуйте
return from item in collection
where item.Id > 10
select (c)item;
Похоже, все, что вам не хватает, это приведение из b в c . Возможно, это связано с использованием .Выберите метод расширения компилятор может обработать это приведение автоматически. Я видел, как компилятор использовал правильный универсальный метод, основанный на возвращаемом типе. Поэтому вместо того, чтобы говорить MyItem item = GenericMethod<MyItem>(input)
, можно сказать MyItem item = GenericMethod(input)
, и компилятор знает, какой тип использовать.
Комментарии:
1. Это изменяет тип выражения выбора и приведет к сбою, поскольку не может найти соответствующий метод выбора.