C#4.0隐式类型动态对象

时间:2010-12-03 20:46:20

标签: c# linq dynamic .net-4.0 anonymous-types

数据文件: 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 个答案:

答案 0 :(得分:6)

您可以使用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&lt; T1,T2&gt ;.

答案 1 :(得分:3)

你真的应该这样做:

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 :(得分:2)

我摆脱了第一行,然后分配列表的地方用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);
}

答案 3 :(得分:1)

这与共同/逆转有关。

您无法将List<T1>投射到List<T2>。试试

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

并且您收到编译错误&#34;无法隐式转换类型&#39; System.Collections.Generic.List<string>&#39;到&#39; System.Collections.Generic.List<object>&#39;&#34;

这是为了防止错误

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

答案 4 :(得分:1)

您无法简单分配的原因如下:

在第一种情况下,您获得List<SomeAnonymousType>。该类型与List<dynamic>完全无关,即使存在从匿名类型到dynamic的隐式转换。这有点像将List<int>隐式转换为List<float>

答案 5 :(得分:0)

动态不是你的解决方案。您只需计算using语句中的区域。

如果你想在这里引入延迟执行,那么在using语句的这样做是个坏主意;在调用Sum之前,您将最终放弃StreamReader。

最后,您的代码应如下所示:

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);
} 

这样做更好,你没有实现列表然后再循环遍历它,你正在拉取数据并根据需要对它进行求和。

答案 6 :(得分:0)

我主要赞同其他评论,动态可能不是这里,但是讨论,但正如Albin指出的那样,除非使用'in'或'out'修饰符明确指定,否则它将与通用参数保持不变

您可以在调用.ToList()之前添加.Cast()来使您的代码工作