Подтвердить что ты не робот

Ошибка времени компиляции с помощью 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.

4b9b3361

Ответ 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. Конечно, это невозможно, поэтому он сообщает о вашей ошибке.