C # 4.0 Неявно Типизированные Динамические Объекты - PullRequest
5 голосов
/ 03 декабря 2010

Файл данных: ( Data.txt ) строки представляют width height

5
6 9
7 2
4 4

C # Код:

var list = new List<dynamic>();
using (var sr = new StreamReader("Data.txt", Encoding.UTF8))
{
    list = sr.ReadToEnd().Split('\n').Select(r =>
    {
        var split = r.Split(' ');
        var len = split.Length;
        return new {
            w = len > 0 ? int.Parse(split[0].Trim()) : 0,
            h = len > 1 ? int.Parse(split[1].Trim()) : 0 
        } as dynamic;
    }).ToList();
}
int Area = list.Sum(r => r.h * r.w);

Пример работает как есть.Мне пришлось сделать несколько нежелательных вещей, чтобы заставить его работать.

Сначала я должен был объявить список, чтобы избежать использования области видимости - поскольку у меня нет типизированного объекта измерения, я сделал тип динамическим (var list = new List<dynamic>()).

Нежелательной частью является приведение анонимного объекта к динамическому (as dynamic).В противном случае я получаю

Не могу неявным образом преобразовать тип System.Collections.Generic.List<AnonymousType#1> в System.Collections.Generic.List<dynamic>

Почему я получаю эту ошибку?Я знаю, что динамический может содержать анонимный тип, так что это проблема с расширением и динамикой ToList()

Мне нужно иметь доступ к элементам анонимного списка вне оператора using,как в последней строке, которая вычисляет площадь.


Решение: Я пошел с ответом dtb.Это позволило избежать использования заявления об использовании и динамики всего вместе.Спасибо всем за вклад!

var list = 
    (from line in File.ReadLines("Data.txt")
    let parts = line.Split(' ')
    let width = int.Parse(parts[0])
    let height = parts.Length > 1 ? int.Parse(parts[1]) : 0
    select new { width, height }).ToList();

Ответы [ 7 ]

6 голосов
/ 03 декабря 2010

Вы можете использовать File.ReadLines , чтобы избежать StreamReader.

IEnumerable<dynamic> query =
    from line in File.ReadLines("Data.txt")
    let parts = line.Split(' ')
    let width = int.Parse(parts[0])
    let height = parts.Length > 1 ? int.Parse(parts[1]) : 0
    select new { width, height } as dynamic;

List<dynamic> list = query.ToList();

int area = list.Sum(t => t.width * t.height);

Однако, как отмечали другие, использование динамического здесь не совсем уместно.Если вы используете запрос только внутри метода, достаточно анонимного экземпляра.Если вы хотите использовать результат запроса вне метода, создайте небольшую структуру или класс или используйте Tuple.

3 голосов
/ 03 декабря 2010

Вы действительно должны сделать что-то вроде этого:

private IEnumerable<Tuple<int, int>> ReadFile(string filePath, 
    Encoding encoding)
{
    using (var sr = new StreamReader(filePath, encoding))
    {
        string line;
        while ((line = sr.ReadLine()) != null) 
        {
            var split = line.Split(' ');
            var w = split.Length > 0 ? int.Parse(split[0]) : 0;
            var h = split.Length > 1 ? int.Parse(split[1]) : 0;
            yield return Tuple.Create(h, w);
        }
    }
}

Теперь у вас есть ленивая и строго типизированная последовательность значений.

2 голосов
/ 03 декабря 2010

Я бы избавился от первой строки, затем, где список назначен, объявил бы его с помощью var и переместил бы последнюю строку внутри оператора using:

int Area;
using (var sr = new StreamReader("Data.txt", Encoding.UTF8))
{
    var list = sr.ReadToEnd().Split('\n').Select(r =>
    {
        var split = r.Split(' ');
        var len = split.Length;
        return new {
            w = len > 0 ? int.Parse(split[0].Trim()) : 0,
            h = len > 1 ? int.Parse(split[1].Trim()) : 0 
        };
    }).ToList();
    Area = list.Sum(r => r.h * r.w);
}
1 голос
/ 03 декабря 2010

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

В первом случае вы получаете List<SomeAnonymousType>. Этот тип совершенно не связан с List<dynamic>, даже если существует неявное преобразование из анонимного типа в dynamic. Это похоже на неявное преобразование List<int> в List<float>.

1 голос
/ 03 декабря 2010

Это связано с ко / контравариантностью.

Вы не можете разыграть List<T1> до List<T2>.Попробуйте с

List<object> objectList = new List<string>{"hello"};

, и вы получите ошибку компиляции "Невозможно неявно преобразовать тип 'System.Collections.Generic.List<string>' в 'System.Collections.Generic.List<object>'"

Это предотвращает ошибку

List<string> stringList = new List<string>{"hello"};
List<object> objectList = stringList; // compile error
objectList.Add(new Car()); // this would add a Car object to the stringList if the above line was allowed
0 голосов
/ 23 апреля 2013

Я в основном согласен с другими замечаниями, динамические, вероятно, не здесь, но для обсуждения, но, как отмечает Альбин, это связано с тем, что общие параметры являются инвариантными, если они явно не указаны с модификаторами «in» или «out» .

Вы можете заставить свой код работать, добавив .Cast () перед вызовом .ToList ()

0 голосов
/ 03 декабря 2010

Динамический не ваше решение здесь.Вам просто нужно вычислить площадь внутри оператора using.

Выполнение этого за пределами оператора using - плохая идея, если вы пытаетесь ввести отсроченное выполнение здесь;вы закончите дипозингом StreamReader перед тем, как совершить вызов Sum.

В конце ваш код должен выглядеть так:

int Area = 0;

using (var sr = new StreamReader("Data.txt", Encoding.UTF8)) 
{ 
    Area = sr.ReadToEnd().Split('\n').Select(r => 
    { 
        var split = r.Split(' '); 
        var len = split.Length; 
        return new { 
            w = len > 0 ? int.Parse(split[0].Trim()) : 0, 
            h = len > 1 ? int.Parse(split[1].Trim()) : 0  
        } as dynamic; 
    }).Sum(r => r.h * r.w);
} 

Делая это лучше, выне материализуете список, а затем снова просматриваете его, вы извлекаете данные и суммируете их по мере необходимости.

...