Ошибка времени компиляции с помощью LINQ Выберите в IEnumerable <dynamic>
Подробнее см. ниже:
У меня есть такой код:
void Test(IEnumerable x)
{
var dynX = x.Cast<dynamic>();
var result = dynX.Select(_ => _.Text);
}
в существующем проекте библиотеки, ориентированном на .NET 4.5. VS2015s IntelliSense подчеркивает часть Text
, жалуясь, что: "object" не содержит определения для "Text"...
Конечно, компиляция завершилась неудачей с
ошибка CS1061: "объект" не содержит определения для "Текст" и не может быть найден метод расширения "Текст", принимающий первый аргумент типа "объект" (вам не хватает директивы using или ссылки на сборку?)
Это сообщение всегда говорит 'object'
, даже когда я меняю приведение на .Cast<IAsyncResult>()
или еще что-то. Когда я нахожу параметр лямбда, всплывающая подсказка показывает его тип IColumn
(который существует, но не имеет отношения). Опять же, какой бы тип я ни выбрал.
Однако, когда я нахожу метод Select()
, он правильно показывает параметр как Func<dynamic, dynamic>
. Если я задаю тип параметра лямбда явно, он компилируется. Если я укажу параметры типа на Select()
явно, это тоже работает.
Другие действия LINQ с dynamic
работают. Когда я копирую этот метод в другой (существующий) проект в решении, он также компилируется. Когда я копирую его в другой файл в том же проекте, он не компилируется.
Он также компилируется с VS2013.
Сама же ошибка появляется и для всех моих коллег, как в Windows 8.1, так и в Windows 10.
Возможно, это какая-то странная проблема с типом вывода...?
Вещи Ive пробовали, что не помогли:
- Создайте новый проект библиотеки .NET 4.5 и снова добавьте файлы и отсутствующие ссылки
- Сравнить (оригинальные) файлы проекта - никаких различий, кроме упорядочения элементов
Обновление
Ну, мне удалось создать автономный минимальный неудачный пример:
static class Program
{
static void Main(string[] args)
{
IEnumerable x = new object[0];
IEnumerable<dynamic> dynX = x.Cast<dynamic>();
// CS1061 'object' does not contain a definition for 'Text'...
// var tooltip shows IColumn instead of IEnumerable<dynamic>
var result = dynX.Select(_ => _.Text);
}
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
}
public interface IColumn { }
Из того, как я это вижу, это явно указывает на серьезную ошибку в том, как VS2015/новая версия компилятора разрешает методы расширения.
Ниже приведено только слабое отношение и в основном относится к вводящим в заблуждение сообщениям об ошибках. Я решил оставить его, чтобы не комментировать ошибки.
Хуже того, они также терпят неудачу с той же ошибкой, хотя ни один IEnumerable
и object
не может иметь метод расширения Select()
:
// CS1061 'object' does not contain a definition for 'Text'
// var tooltip shows IColumn
var result2 = x.Select(_ => _.Text);
object o = new object();
// CS1061 'object' does not contain a definition for 'Text'
// var tooltip shows IColumn
var result3 = o.Select(_ => _.Text);
Добавление
Эта проблема теперь отслеживается в отслеживании ошибок Roslyn.
Ответы
Ответ 1
Хорошо, посмотрев, как отчет об ошибке был разрешен довольно долгое время, давайте подытожим его:
Это была ошибка, компилятор не применил флаг dynamic
, как должен, в результате чего он становится object
. Исправлена ошибка. Я не знаю, когда он будет доступен в VS2015, возможно, кто-то еще может предоставить эту информацию.
Вероятно, это вызвало некоторые причуды в механизме разрешения перегрузки, что привело к ошибочным сообщениям об ошибках и содержимому всплывающей подсказки.
Ответ 2
Не могу сказать, почему он работает в одном VS, а не в другом, но это то, что я сделал бы
Rename.
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
"Выбрать" - это сборка в имени метода для другой общей библиотеки.
поэтому я настоятельно рекомендую вам переименовать его в нечто вроде "AppColumnSelectText" или что-то, но не "Выбрать".
затем измените
public interface IColumn { }
To
public interface IColumn
{
string Text {get; set;}
}
затем реализуем его
public class MyClass : IColumn
{
public string Text { get; set;}
}
Затем переведите динамику в IColumn, предположив, что она получена из класса класса MyClass
var columns = fields.Select(a => new {a as IColumn}).ToList();
Тогда вы сможете сделать
var result3 = columns.AppColumnSelectText(x => x.Text);
Я знаю, что это, вероятно, не то, что вы ищете, но программирование чище, и вы можете архивировать тот же результат.
UPDATE
Прочтите ниже, и мы надеемся, что это красит лучшую картину.
public static class Test
{
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
public static IColumn SelectOtherColumn<TResult>(this IColumn source, Func<IColumn, TResult> selector)
{
throw new NotImplementedException();
}
}
public interface IColumn
{
string Text { get; set; }
}
public class Program
{
private static void Main(string[] args)
{
IEnumerable ojb = new object[0];
IEnumerable<dynamic> dynX = ojb.Cast<dynamic>();
// CS1061 'object' does not contain a definition for 'Text'...
// var tooltip shows IColumn instead of IEnumerable<dynamic>
//NB this is the System.Linq.Select
var result = dynX.Select(x => x.Text);
var xzy = dynX as IColumn;
//converstion here will probably FAIL so this makes this pointless.
//here the compliter complains as the Type object has no Text Prop as it was not sepcified anywhere
var theThingyouwant1 = xzy.Select(x => x.Text);
//here you are OK as the complier can infer something
var theThingyouwant2 = xzy.SelectOtherColumn(x => x.Text);
}
}
Обновление
В дополнение к этому... См. Ниже для иллюстрации.
public class MyType
{
public string Text { get; set; }
public string SomethingEsle { get; set; }
}
public class Program
{
private static void Main(string[] args)
{
List<MyType> ojb = new List<MyType>();
ojb.Add(new MyType {Text = "OMG", SomethingEsle = "cat"});
//dynX is a dynamic...
var dynX = ojb.Select(x => new {x.Text, x.SomethingEsle}).ToList();
//NB this is the System.Linq.Select
//this now works as the complier can determine that there is a property Text
var result = dynX.Select(x => x.Text).ToList();
}
}
Вы можете использовать свой выбор... но он работает только на том, что реализует IColumn, поэтому мне трудно понять, как это будет работать.
Ответ 3
Вы можете использовать этот
static void Main(string[] args)
{
//IEnumerable x = new object[0];
//var result2 = x.Select(_ => _.Text);
//Compile time Error "Enumerable" does not contain a definition for 'Select' and no extension method
// because IEnumerable is not a generic class
IEnumerable<object> x = new object[0];
var result2 = x.Select(_ => _.Text);
}
Ответ 4
Это просто случай, когда С# использует форму утиного ввода, чтобы LINQ мог работать для любого типа.
Начну с простого примера.
Если я определяю строку класса Foo
:
public class Foo
{
public int Bar;
public int Select(Func<Foo, int> map)
{
return map(this);
}
}
Затем я могу написать этот код:
Foo foo = new Foo() { Bar = 42 };
int query =
from f in foo
select f.Bar;
Я получаю версию LINQ, которая работает с типом Foo
(not IEnumerable<T>
), который возвращает int
(not IEnumerable<R>
).
Все операторы LINQ могут быть специально определены таким образом.
Вышеприведенный пример также может быть записан следующим образом:
public class Foo
{
public int Bar;
}
public static class Ex
{
public static int Select(this Foo source, Func<Foo, int> selector)
{
return selector(source);
}
}
Теперь он начинает выглядеть как ваш код.
Итак, если мы сделаем это изменение:
public class Foo
{
}
public static class Ex
{
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
}
public interface IColumn { }
Затем этот код выходит из строя с той же ошибкой, что и ваш:
IEnumerable<dynamic> foo = new [] { new Foo() };
var query =
from f in foo
select f.Text;
Если я сделаю это изменение:
public static class Ex
{
public static IColumn Select<TSource, TResult>(this IColumn source, Func<TSource, TResult> selector)
{
throw new NotImplementedException();
}
}
Теперь код сообщает, что "RuntimeBinderException:" Foo "не содержит определения для" Текст ".
Что мы, наконец, сделали, показывает, что компилятор пытается реализовать оператор LINQ Select
с помощью вашего метода Select
. Затем он пытается определить, может ли он использовать определенный метод Select
как перегрузку для представленных типов, и поскольку dynamic
может быть "отброшен" до IColumn
, он говорит, что ваш метод Select
является лучшей перегрузкой. Кроме того, поскольку компилятор будет искать только кандидатов в текущем классе, он не будет искать стандартный оператор LINQ.
Конечно, тогда компилятор, используя вашу перегрузку, отбрасывает dynamic
в object
, а затем пытается найти свойство .Text
. Конечно, это невозможно, поэтому он сообщает о вашей ошибке.