从这个开始:
IEnumerable<KeyValuePair<int, double>> ReadFrames(string path)
{
return File.ReadLines(path).Select(l =>
{
var parts = l.Split(' ').Select(p => p.Trim());
return new KeyValuePair<int, double>(
int.Parse(parts.First()),
double.Parse(parts.Skip(1).First()));
});
}
现在我们有了帧,让我们按位置编号查找帧:
int GetFrameByPosition(IEnumerable<KeyValuePair<int,double>> frames, double position)
{
return frames.SkipWhile(f => f.Value < position).First().Key;
}
请注意,它是单行的。像这样称呼它:
int frameNumber = GetFrameByPosition(GetFrames("path"), 18.10D);
如果您需要回答不同的问题,那也可能是单行的。例如,该代码获取大于您输入的第一帧,但您要求最接近的帧,这可能是此之前的帧。你可以这样做:
int GetNearestFrameByPosition(IEnumerable<KeyValuePair<int,double>> frames, double position)
{
return frames.OrderBy(f => Math.Abs(position - f.Value)).First().Key;
}
另一个例子是,如果您使用它来寻找播放的起始位置,并且您确实希望所有帧都从第一帧开始。很简单:
IEnumerable<KeyValuePair<int,double>> SeekToFrameByPosition(IEnumerable<KeyValuePair<int,double>> frames, double position)
{
return frames.SkipWhile(f => f.Value < frames.OrderBy(f => Math.Abs(position - f.Value)).First().Key);
}
还是单线。
这里唯一的弱点是,如果你每次都返回文件,它每次都会从磁盘读取,这很慢。这可能是您需要的,但如果您不需要这样做,可以通过将所有帧预先加载到内存中来轻松提高速度,如下所示:
var cachedFrames = ReadFrames("path").ToList();
然后只需在任何地方使用该 cachedFrames 变量,而不是重新调用 ReadFrames() 函数。
最后,有一种思想流派会避免使用 KeyValuePair 来支持创建自定义类。该类可能如下所示:
public class Frame
{
public int index {get;set;}
public double position {get;set;}
}
在上面看到的任何地方都使用它KeyValuePair<int,double>
。此外,它足够小(< 16 字节),您可以考虑使用结构而不是类。如果你确实使用了一个结构,最好也让它成为不可变的,这是一种奇特的说法,你在构造函数中设置了成员,然后以后再也不改变它们:
public struct Frame
{
public Frame(int index, double position)
{
this.index = index;
this.position = position;
}
public int index {get;private set;}
public double position {get;private set;}
}