使用 IEnumerable 作为只读列表是否更好?
Is it better to use IEnumerable as read only list?
我正在从远程服务器下载 .tgz
文件到本地文件夹,然后将其解压缩。之后,我读取了内存中的所有 json/txt
个文件。下面是我的代码:
public IEnumerable<DataHolder> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess.Result) { yield return default; }
// this will unzip files in same directory
var isUnzipSuccess = UnzipTgzFile(_directoryToDownload, fileName);
if (!isUnzipSuccess) { yield return default; }
// this will get list of all files in same directory
IList<string> files = GetListOfFiles(_directoryToDownload);
if (files == null || files.Count == 0) { yield return default; }
// total files will be 500 max
for (int i = 0; i < files.Count; i++)
{
var cfgPath = files[i];
if (!File.Exists(cfgPath)) { continue; }
var fileDate = File.GetLastWriteTimeUtc(cfgPath);
var fileContent = File.ReadAllText(cfgPath);
var pathPieces = cfgPath.Split(System.IO.Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var fileName = pathPieces[pathPieces.Length - 1];
var md5Hash = CheckMD5(cfgPath);
yield return new DataHolder
{
FileName = fileName,
FileDate = fileDate,
FileContent = fileContent,
FileMD5HashValue = md5Hash
};
}
}
用例:
- 如果我无法成功下载文件(
isDownloadSuccess
为 false),那么我想 return 清空 IEnumerable。
- 如果我无法成功解压缩文件(
isUnzipSuccess
是假的)那么我想 return 也清空 IEnumerable。
- 如果我无法成功获取文件列表(
files
列表为空),那么我也想 return 清空 IEnumerable。
- 如果我在 for 循环中遇到一些处理问题,那么我也想 return 清空 IEnumerable。
- 否则只是 return readonly IEnumerable 将数据返回给调用者。
我在使用上述方法时遇到的问题是 - 我无法在 returns yield return default
的情况下进行空检查,而且我对 for 循环中处理失败会发生什么感到困惑, return 清空 IEnumerable
也会返回给调用者吗?
IEnumerable<DataHolder> dataHolders = GetFiles(fileName);
// below check doesn't work on negative cases
if (dataHolders == null || !dataHolders.Any())
return false;
//....
那么这是在这里使用 IEnumerable
的正确方法吗?或者我可以使用任何其他数据结构,它可以向调用者提供 只读列表 以及空列表(对于负面情况)我可以轻松检查是否为 null 或空。
问题:
我的目标只是 return read only list
将数据返回给用户(对于正面案例)。对于所有负面情况,我需要 return 将空的只读列表返回给用户。
我们在聊天中谈过,但会重申。
Yield 在这里不起作用,因为出于任何特定原因我们并不真正需要这些语义。您想获得一个文件列表,以便稍后用于与其他文件列表进行比较(它们最终都必须读入内存,现在也可以这样做):
public IReadOnlyList<DataHolder> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess.Result) { return Array.Empty<DataHolder>(); }
// this will unzip files in same directory
var isUnzipSuccess = UnzipTgzFile(_directoryToDownload, fileName);
if (!isUnzipSuccess) { return Array.Empty<DataHolder>(); }
// this will get list of all files in same directory
IList<string> files = GetListOfFiles(_directoryToDownload);
if (files == null || files.Count == 0) { return Array.Empty<DataHolder>(); }
var lst = new List<DataHolder>(files.Count);
for (int i = 0; i < files.Count; i++)
{
var cfgPath = files[i];
if (!File.Exists(cfgPath)) { continue; }
var fileDate = File.GetLastWriteTimeUtc(cfgPath);
var fileContent = File.ReadAllText(cfgPath);
var pathPieces = cfgPath.Split(System.IO.Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var fileName = pathPieces[pathPieces.Length - 1];
var md5Hash = CheckMD5(cfgPath);
lst.Add(new DataHolder
{
FileName = fileName,
FileDate = fileDate,
FileContent = fileContent,
FileMD5HashValue = md5Hash
});
}
return lst.AsReadOnly();
}
我们现在只返回一个包含您所有项目的 read-only 列表,这样您就可以检查是否存在任何项目,例如:
if(lst?.Count > 0){ /* There are items to process */ }
此外,这不会破坏您的模式,因为 IReadOnlyList
实现了 IEnumerable
,因此它会非常适合。
由于您一次下载并解压缩了所有文件,我知道您并不担心此实现是一个实际可迭代的(因为 foreach
会等到一切都完成才能迭代) .
记住这一点,最简单的方法就是去掉 yields
和 return 数组。
实施示例(可能需要一些拼写检查):
public IEnumerable<DataHolder> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess.Result) { return Array.Empty<DataHolder>(); }
// this will unzip files in same directory
var isUnzipSuccess = UnzipTgzFile(_directoryToDownload, fileName);
if (!isUnzipSuccess) { return Array.Empty<DataHolder>(); }
// this will get list of all files in same directory
IList<string> files = GetListOfFiles(_directoryToDownload);
if (files == null || files.Count == 0) { return Array.Empty<DataHolder>(); }
var data = new DataHolder[files.Count];
try
{
for (int i = 0; i < files.Count; i++)
{
var cfgPath = files[i];
if (!File.Exists(cfgPath)) { continue; }
var fileDate = File.GetLastWriteTimeUtc(cfgPath);
var fileContent = File.ReadAllText(cfgPath);
var pathPieces = cfgPath.Split(System.IO.Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var fileName = pathPieces[pathPieces.Length - 1];
var md5Hash = CheckMD5(cfgPath);
data[i] = new DataHolder
{
FileName = fileName,
FileDate = fileDate,
FileContent = fileContent,
FileMD5HashValue = md5Hash
};
}
return data;
}
catch (Exception ex)
{
return Array.Empty<DataHolder>();
}
}
为了消费这个,你会,例如:
var files = GetFiles("somename.txt");
if (!files.Any()) // do not check for files being null
{
return;
}
旁注,我会把前几行改成这样,所以你不要做 sync-over-async 这会导致死锁:
public async Task<IEnumerable<DataHolder>> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = await DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess) { return Array.Empty<DataHolder>(); }
...
}
我正在从远程服务器下载 .tgz
文件到本地文件夹,然后将其解压缩。之后,我读取了内存中的所有 json/txt
个文件。下面是我的代码:
public IEnumerable<DataHolder> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess.Result) { yield return default; }
// this will unzip files in same directory
var isUnzipSuccess = UnzipTgzFile(_directoryToDownload, fileName);
if (!isUnzipSuccess) { yield return default; }
// this will get list of all files in same directory
IList<string> files = GetListOfFiles(_directoryToDownload);
if (files == null || files.Count == 0) { yield return default; }
// total files will be 500 max
for (int i = 0; i < files.Count; i++)
{
var cfgPath = files[i];
if (!File.Exists(cfgPath)) { continue; }
var fileDate = File.GetLastWriteTimeUtc(cfgPath);
var fileContent = File.ReadAllText(cfgPath);
var pathPieces = cfgPath.Split(System.IO.Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var fileName = pathPieces[pathPieces.Length - 1];
var md5Hash = CheckMD5(cfgPath);
yield return new DataHolder
{
FileName = fileName,
FileDate = fileDate,
FileContent = fileContent,
FileMD5HashValue = md5Hash
};
}
}
用例:
- 如果我无法成功下载文件(
isDownloadSuccess
为 false),那么我想 return 清空 IEnumerable。 - 如果我无法成功解压缩文件(
isUnzipSuccess
是假的)那么我想 return 也清空 IEnumerable。 - 如果我无法成功获取文件列表(
files
列表为空),那么我也想 return 清空 IEnumerable。 - 如果我在 for 循环中遇到一些处理问题,那么我也想 return 清空 IEnumerable。
- 否则只是 return readonly IEnumerable 将数据返回给调用者。
我在使用上述方法时遇到的问题是 - 我无法在 returns yield return default
的情况下进行空检查,而且我对 for 循环中处理失败会发生什么感到困惑, return 清空 IEnumerable
也会返回给调用者吗?
IEnumerable<DataHolder> dataHolders = GetFiles(fileName);
// below check doesn't work on negative cases
if (dataHolders == null || !dataHolders.Any())
return false;
//....
那么这是在这里使用 IEnumerable
的正确方法吗?或者我可以使用任何其他数据结构,它可以向调用者提供 只读列表 以及空列表(对于负面情况)我可以轻松检查是否为 null 或空。
问题:
我的目标只是 return read only list
将数据返回给用户(对于正面案例)。对于所有负面情况,我需要 return 将空的只读列表返回给用户。
我们在聊天中谈过,但会重申。
Yield 在这里不起作用,因为出于任何特定原因我们并不真正需要这些语义。您想获得一个文件列表,以便稍后用于与其他文件列表进行比较(它们最终都必须读入内存,现在也可以这样做):
public IReadOnlyList<DataHolder> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess.Result) { return Array.Empty<DataHolder>(); }
// this will unzip files in same directory
var isUnzipSuccess = UnzipTgzFile(_directoryToDownload, fileName);
if (!isUnzipSuccess) { return Array.Empty<DataHolder>(); }
// this will get list of all files in same directory
IList<string> files = GetListOfFiles(_directoryToDownload);
if (files == null || files.Count == 0) { return Array.Empty<DataHolder>(); }
var lst = new List<DataHolder>(files.Count);
for (int i = 0; i < files.Count; i++)
{
var cfgPath = files[i];
if (!File.Exists(cfgPath)) { continue; }
var fileDate = File.GetLastWriteTimeUtc(cfgPath);
var fileContent = File.ReadAllText(cfgPath);
var pathPieces = cfgPath.Split(System.IO.Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var fileName = pathPieces[pathPieces.Length - 1];
var md5Hash = CheckMD5(cfgPath);
lst.Add(new DataHolder
{
FileName = fileName,
FileDate = fileDate,
FileContent = fileContent,
FileMD5HashValue = md5Hash
});
}
return lst.AsReadOnly();
}
我们现在只返回一个包含您所有项目的 read-only 列表,这样您就可以检查是否存在任何项目,例如:
if(lst?.Count > 0){ /* There are items to process */ }
此外,这不会破坏您的模式,因为 IReadOnlyList
实现了 IEnumerable
,因此它会非常适合。
由于您一次下载并解压缩了所有文件,我知道您并不担心此实现是一个实际可迭代的(因为 foreach
会等到一切都完成才能迭代) .
记住这一点,最简单的方法就是去掉 yields
和 return 数组。
实施示例(可能需要一些拼写检查):
public IEnumerable<DataHolder> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess.Result) { return Array.Empty<DataHolder>(); }
// this will unzip files in same directory
var isUnzipSuccess = UnzipTgzFile(_directoryToDownload, fileName);
if (!isUnzipSuccess) { return Array.Empty<DataHolder>(); }
// this will get list of all files in same directory
IList<string> files = GetListOfFiles(_directoryToDownload);
if (files == null || files.Count == 0) { return Array.Empty<DataHolder>(); }
var data = new DataHolder[files.Count];
try
{
for (int i = 0; i < files.Count; i++)
{
var cfgPath = files[i];
if (!File.Exists(cfgPath)) { continue; }
var fileDate = File.GetLastWriteTimeUtc(cfgPath);
var fileContent = File.ReadAllText(cfgPath);
var pathPieces = cfgPath.Split(System.IO.Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
var fileName = pathPieces[pathPieces.Length - 1];
var md5Hash = CheckMD5(cfgPath);
data[i] = new DataHolder
{
FileName = fileName,
FileDate = fileDate,
FileContent = fileContent,
FileMD5HashValue = md5Hash
};
}
return data;
}
catch (Exception ex)
{
return Array.Empty<DataHolder>();
}
}
为了消费这个,你会,例如:
var files = GetFiles("somename.txt");
if (!files.Any()) // do not check for files being null
{
return;
}
旁注,我会把前几行改成这样,所以你不要做 sync-over-async 这会导致死锁:
public async Task<IEnumerable<DataHolder>> GetFiles(string fileName)
{
// this will download files to a directory
var isDownloadSuccess = await DownloadFiles(_url, fileName, _directoryToDownload);
if (!isDownloadSuccess) { return Array.Empty<DataHolder>(); }
...
}