dynamic, linq и Select () - PullRequest
       23

dynamic, linq и Select ()

13 голосов
/ 04 мая 2011

Учитывая следующий (бессмысленно, но для наглядности) тестовый класс:

public class Test
{
    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(x => ToStr(x));
    }

    public IEnumerable<string> ToEnumerableStrsWillCompile(IEnumerable<dynamic> t)
    {
        var res = new List<string>();

        foreach (var d in t)
        {
            res.Add(ToStr(d));
        }

        return res;
    }

    public string ToStr(dynamic d)
    {
        return new string(d.GetType());
    }
}

Почему он не компилируется со следующей ошибкой в ​​t.Select(x => ToStr(x))?

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<dynamic>' 
to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion 
exists (are you missing a cast?)

Нет ошибки по второму способу.

Ответы [ 4 ]

11 голосов
/ 04 мая 2011

Я полагаю, что здесь происходит то, что, поскольку выражение ToStr(x) включает переменную dynamic, тип результата всего выражения также dynamic; вот почему компилятор считает, что у него есть IEnumerable<dynamic>, где он ожидает IEnumerable<string>.

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(x => ToStr(x));
}

Есть два способа исправить это.

Используйте явное приведение:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(x => (string)ToStr(x));
}

Это говорит компилятору, что результатом выражения определенно будет строка, поэтому в итоге мы получим IEnumerable<string>.

Заменить лямбду группой методов:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(ToStr);
}

Таким образом, компилятор неявно преобразует выражение группы методов в лямбду. Обратите внимание, что поскольку в выражении нет упоминания о переменной dynamic x, тип ее результата может быть сразу определен как string, поскольку существует только один метод для рассмотрения, а его возвращаемый тип - * 1027. *.

1 голос
/ 04 мая 2011

может показаться, что компилятор C # определяет тип лямбды в первом методе x => ToStr(x) как Func<dynamic, dynamic> и, следовательно, объявляет тип IEnumerable, возвращаемый как IEnumerable<dynamic>.Небольшое изменение x => (string)ToStr(x), кажется, исправляет это.

Это наиболее вероятно из-за правил вывода типов - потому что если вы измените строку на это:

return t.Select<dynamic, string>(x => ToStr(x));

Это компилируется без ошибок.

Правило вывода конкретного типа, о котором идет речь, однако я не слишком уверен в этом - однако, если вы возьмете следующий код:

public void foo(dynamic d)
{
  var f = this.ToStr(d);
  string s = f;
}

А затем наведите курсор на 'f' вВ редакторе вы увидите, что intellisense сообщает тип выражения как «динамический f».Это будет связано с тем, что this.ToStr(d) является динамическим выражением, независимо от того, известен ли сам метод и его тип возвращаемого значения во время компиляции.

Затем компилятор с радостью назначит string s = f;, поскольку онвозможность статического анализа типа, который, вероятно, будет f, потому что в конечном итоге ToStr всегда возвращает строку.

Вот почему этот первый метод требует приведение или явные параметры типа - потому что компилятор делаетToStr быть типа dynamic;потому что в нем есть хотя бы одно динамическое выражение.

1 голос
/ 04 мая 2011

Попробуйте return t.Select(x => ToStr(x)) as IEnumerable<string>

1 голос
/ 04 мая 2011

Попробуйте так:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(ToStr);
}

Другая возможность состоит в том, чтобы явно указать общие аргументы:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select<dynamic, string>(x => ToStr(x));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...