数据文件:( 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();
答案 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()来使您的代码工作