C# 在闭包中捕获TextReader但仍正确处理它的好方法是什么?
这是一个简化的示例,用于分离问题的目的。在我实际的scneario中,GetColumnReader返回的ColumnReader实际上要做的工作不仅仅是ReadLine 如果我运行下面的程序,当我试图调用Reader()时,我会得到一个错误,因为当然,文本阅读器已经被using语句处理掉了C# 在闭包中捕获TextReader但仍正确处理它的好方法是什么?,c#,.net,lambda,closures,C#,.net,Lambda,Closures,这是一个简化的示例,用于分离问题的目的。在我实际的scneario中,GetColumnReader返回的ColumnReader实际上要做的工作不仅仅是ReadLine 如果我运行下面的程序,当我试图调用Reader()时,我会得到一个错误,因为当然,文本阅读器已经被using语句处理掉了 public class Play{ delegate string ColumnReader(); static ColumnReader GetColumnReader(string f
public class Play{
delegate string ColumnReader();
static ColumnReader GetColumnReader(string filename){
using (TextReader reader = new StreamReader(filename)){
var headers = reader.ReadLine();
return () => reader.ReadLine();
}
}
public static void Main(string[] args){
var Reader = GetColumnReader("Input.tsv");
Console.WriteLine(Reader());
}
}
或者,我可以删除“using”并直接声明TextReader,这将起作用,但现在我们不再保证TextReader最终会关闭
有没有办法在返回的lambda函数中添加一个“析构函数”,以便在lambda函数超出范围(没有更多引用)时,我可以立即处理TextReader
我也欢迎其他建议,但希望保留基本的闭包结构(即,符合问题的范围)。如果不需要lambda表达式,可以创建可枚举的 在=>{}中使用移动可能在实际代码中起作用。。。仍然可能不是您想要的:
static ColumnReader GetColumnReader(string filename) {
return () => {
using (TextReader reader = new StreamReader(filename)) {
var headers = reader.ReadLine();
return reader.ReadLine();
}
};
}
带有IEnumerable的版本(如果始终完成迭代):
静态IEnumerable GetColumnReader(字符串文件名){
使用(TextReader=new StreamReader(“aa”)){
var headers=reader.ReadLine();
产生返回reader.ReadLine();
}
}
如果希望支持枚举中间的迭代,则需要创建自定义IDisposable迭代器。查看如何处理此类情况。本质上,您需要委托本身之外的一次性元素的作用域。在这些情况下,我会让代理接受一次性实例(即TextReader),而不是文件名
public class Play {
delegate string ColumnReader();
static ColumnReader GetColumnReader(string filename) {
return () => {
using (TextReader reader = new StreamReader(filename)) {
var headers = reader.ReadLine();
return reader.ReadLine();
}
};
}
public static void Main(string[] args) {
var Reader = GetColumnReader("Input.tsv");
Console.WriteLine(Reader());
}
}
显然,每次调用返回的委托时,这将打开/读取一行/关闭文件
如果需要打开它一次,然后在阅读几行时保持打开状态,最好使用迭代器块,如下所示:
public class Play {
static IEnumerable<string> ReadLines(string filename) {
using (TextReader reader = new StreamReader(filename)) {
var headers = reader.ReadLine(); // I'm guessing you want to ignore this??
while (true) {
string line = reader.ReadLine();
if (line == null)
yield break;
yield return line;
}
}
}
public static void Main(string[] args) {
foreach (string line in ReadLines("Input.tsv"))
Console.WriteLine(line);
}
}
公共类游戏{
静态IEnumerable可读行(字符串文件名){
使用(TextReader=new StreamReader(文件名)){
var headers=reader.ReadLine();//我猜您想忽略这个??
while(true){
字符串行=reader.ReadLine();
如果(行==null)
屈服断裂;
收益率回归线;
}
}
}
公共静态void Main(字符串[]args){
foreach(ReadLines中的字符串行(“Input.tsv”))
控制台写入线(行);
}
}
如果您确实想保留闭包语义,则需要为其添加一个参数。类似于bellow,但您必须注意调用dispose命令
public class Play {
enum ReaderCommand {
Read,
Close
}
delegate string ColumnReader(ReaderCommand cmd);
static ColumnReader GetColumnReader(string filename) {
TextReader reader = new StreamReader(filename);
var headers = reader.ReadLine();
return (ReaderCommand cmd) => {
switch (cmd) {
case ReaderCommand.Read:
return reader.ReadLine();
case ReaderCommand.Close:
reader.Dispose();
return null;
}
return null;
};
}
public static void Main(string[] args) {
var Reader = GetColumnReader("Input.tsv");
Console.WriteLine(Reader(ReaderCommand.Read));
Console.WriteLine(Reader(ReaderCommand.Read));
Reader(ReaderCommand.Close);
Console.ReadKey();
}
}
这比简单地返回文本阅读器更容易吗?在我看来,你只是为了达到一种特定的编码风格而使事情变得更加复杂 调用方始终有责任正确处理返回的任何内容。
我相信你的项目会给你足够的机会来锻炼你的肌肉——这次只是简单一点 我非常喜欢收益率解决方案。我已经编写了一个简单的代码,它表明它工作得很好,资源可以被释放,在客户端为每个
static void Main(string[] args)
{
using (Resource resource = new Resource())
{
foreach (var number in resource.GetNumbers())
{
if (number > 2)
break;
Console.WriteLine(number);
}
}
Console.Read();
}
public class Resource : IDisposable
{
private List<int> _numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
public IEnumerable<int> GetNumbers()
{
foreach (var number in _numbers)
yield return number;
}
public void Dispose()
{
Console.WriteLine("Resource::Dispose()...");
}
}
static void Main(字符串[]args)
{
使用(资源=新资源())
{
foreach(resource.GetNumbers()中的变量编号)
{
如果(数量>2)
打破
控制台写入线(编号);
}
}
Console.Read();
}
公共类资源:IDisposable
{
私人名单编号=新名单{1,2,3,4,5,6,7};
公共IEnumerable GetNumbers()
{
foreach(变量编号在_编号中)
收益返回数;
}
公共空间处置()
{
WriteLine(“Resource::Dispose()…”;
}
}
1)使用{}是一种很好的处理方法2)我看不到Reader()的实现@HatSoft,2?“var Reader=GetColumnReader”。。。局部变量名的大小写显然不寻常,但它仍然有效……您需要调用Reader()闭包一次还是多次?@Smilediver,我可能需要调用多次,但在文件完成之前不一定。请改用迭代器。我喜欢这个解决方案,因为它直观地表明,一次性文件的范围由最终使用它的方法控制……也就是说,调用闭包的方法。
static void Main(string[] args)
{
using (Resource resource = new Resource())
{
foreach (var number in resource.GetNumbers())
{
if (number > 2)
break;
Console.WriteLine(number);
}
}
Console.Read();
}
public class Resource : IDisposable
{
private List<int> _numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
public IEnumerable<int> GetNumbers()
{
foreach (var number in _numbers)
yield return number;
}
public void Dispose()
{
Console.WriteLine("Resource::Dispose()...");
}
}