我的应用程序遍历目录树,在每个目录中尝试使用 File.OpenRead()
打开特定名称的文件。 如果此调用引发了 FileNotFoundException
,则它知道该文件不存在。我是否应在此之前进行 File.Exists()
调用以检查文件是否存在?这样做是否更有效率?
我的应用程序遍历目录树,在每个目录中尝试使用 File.OpenRead()
打开特定名称的文件。 如果此调用引发了 FileNotFoundException
,则它知道该文件不存在。我是否应在此之前进行 File.Exists()
调用以检查文件是否存在?这样做是否更有效率?
更新
我在一个循环中运行了这两个方法并计时每个方法:
void throwException()
{
try
{
throw new NotImplementedException();
}
catch
{
}
}
void fileOpen()
{
string filename = string.Format("does_not_exist_{0}.txt", random.Next());
try
{
File.Open(filename, FileMode.Open);
}
catch
{
}
}
void fileExists()
{
string filename = string.Format("does_not_exist_{0}.txt", random.Next());
File.Exists(filename);
}
Random random = new Random();
以下是不使用调试器附加且运行发布版本的结果:
方法 每秒迭代次数 throwException 10100 fileOpen 2200 fileExists 11300
抛出异常的成本比我预期的要高得多,而在文件不存在的情况下调用FileOpen似乎比检查不存在的文件要慢得多。
如果文件通常不存在,则使用检查文件是否存在似乎更快。相反,如果文件通常存在,则发现捕获异常更快。如果性能对您的应用程序至关重要,请在实际数据上对两种方法进行基准测试。
正如其他答案中提到的那样,即使在打开文件之前检查文件是否存在,您也应该注意竞态条件。如果有人在您的存在检查后但在您打开它之前删除了该文件,您仍然需要处理异常。
File.TryOpenRead
是最好的解决方案,但不幸的是,BCL团队没有提供这样一个方法。 - Ben Voigt不要使用File.Exists,这会引入并发问题。如果你写下了这段代码:
if file exists then
open file
如果在你检查 File.Exists 和实际打开文件之间,另一个程序删除了你的文件,那么该程序仍将引发异常。
其次,即使文件存在,也不意味着您可以实际打开该文件,您可能没有权限打开该文件,或者该文件可能是只读文件系统,因此无法以写入模式打开,等等。
文件输入/输出比异常昂贵得多,所以不需要担心异常的性能问题。
编辑: 在 Linux 下以 Python 对异常和存在进行基准测试
import timeit
setup = 'import random, os'
s = '''
try:
open('does not exist_%s.txt' % random.randint(0, 10000)).read()
except Exception:
pass
'''
byException = timeit.Timer(stmt=s, setup=setup).timeit(1000000)
s = '''
fn = 'does not exists_%s.txt' % random.randint(0, 10000)
if os.path.exists(fn):
open(fn).read()
'''
byExists = timeit.Timer(stmt=s, setup=setup).timeit(1000000)
print 'byException: ', byException # byException: 23.2779269218
print 'byExists: ', byExists # byExists: 22.4937438965
CreateFile
),如果它得到一个句柄,就知道成功了,否则就知道失败了。没有竞争条件。 - dsolimano这种行为是否真的是例外情况呢?如果是预期的,你应该使用if语句进行测试,而不是使用异常。性能并不是这个解决方案唯一需要考虑的问题,从你试图做的事情来看,性能不应该是一个问题。因此,风格和一个好的方法应该是这个解决方案关注的重点。
因此,总结一下,既然你期望有些测试会失败,那么使用File.Exists进行检查而不是事后捕获异常。当然,你仍然应该捕获其他可能发生的异常。
File.Exists
是 File.TryOpenRead
的良好替代品。它们并不相同,不能解决问题,所以不要使用它。 - Ben VoigtFile.OpenRead
时,您基本上必须处理http://msdn.microsoft.com/en-us/library/system.io.file.openread.aspx中提到的异常。您所要求的似乎是类似于已检查异常的东西,这在C#中不受支持,并且是一个非常有争议的话题。 - Dirk Vollmar进行目录搜索,找到文件后再尝试打开不是最高效的方式吗?
Dim Files() as string = System.IO.Directory.GetFiles("C:\", "SpecificName.txt", IO.SearchOption.AllDirectories)
那么,您将得到一个字符串数组,您知道它们存在。
哦,作为对原始问题的回答,我会说是的,try/catch会引入更多的处理器周期,我还会假设IO峰值实际上比处理器周期的开销更长。
先运行Exists,然后再运行Open,是2个IO函数,而只尝试打开它则只有1个。因此,我认为整体性能将根据PC上的处理器时间与硬盘速度来判断。如果您的处理器较慢,我会选择检查,如果您的处理器很快,我可能会在这个问题上使用try/catch。
这要看情况!
如果有很大的可能性文件存在(您可以针对您的场景进行判断,例如像 desktop.ini
这样的文件),我会更愿意直接尝试打开它。
但是,如果使用 File.Exist
,由于并发原因和避免任何运行时异常,您需要在 try/catch 中放置 File.OpenRead
,但如果文件存在的可能性较低,则可以极大地提高应用程序性能。参见鸵鸟算法。
File.Exists是一个很好的第一道防线。如果文件不存在,那么如果您尝试打开它,则保证会抛出异常。存在检查比抛出和捕获异常的成本要低。(也许不会便宜太多,但有一点)。
还有另一个考虑因素:调试。当您在调试器中运行时,抛出和捕获异常的成本更高,因为IDE具有钩子进入异常机制,从而增加了您的开销。如果您已经在“调试”>“异常”中勾选了任何“断开抛出”的复选框,则任何可避免的异常都会成为一个巨大的痛点。仅出于这个原因,我会主张在可能的情况下防止异常。
然而,由于时间,权限,太阳耀斑等原因,您仍然需要try-catch,这是其他答案所指出的原因。 File.Exists调用只是一种优化;它不能使您免于需要捕获异常。
是的,你应该使用File.Exists。异常应该用于异常情况,而不是控制程序的正常流程。在你的情况下,文件不存在并不是一个异常情况。因此,你不应该依赖于异常。
更新:
为了让每个人都可以自己尝试,我将发布我的测试代码。对于不存在的文件,依赖于File.Open抛出异常比使用File.Exists检查要慢50倍左右。
class Program
{
static void Main(string[] args)
{
TimeSpan ts1 = TimeIt(OpenExistingFileWithCheck);
TimeSpan ts2 = TimeIt(OpenExistingFileWithoutCheck);
TimeSpan ts3 = TimeIt(OpenNonExistingFileWithCheck);
TimeSpan ts4 = TimeIt(OpenNonExistingFileWithoutCheck);
}
private static TimeSpan TimeIt(Action action)
{
int loopSize = 10000;
DateTime startTime = DateTime.Now;
for (int i = 0; i < loopSize; i++)
{
action();
}
return DateTime.Now.Subtract(startTime);
}
private static void OpenExistingFileWithCheck()
{
string file = @"C:\temp\existingfile.txt";
if (File.Exists(file))
{
using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
{
}
}
}
private static void OpenExistingFileWithoutCheck()
{
string file = @"C:\temp\existingfile.txt";
using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
{
}
}
private static void OpenNonExistingFileWithCheck()
{
string file = @"C:\temp\nonexistantfile.txt";
if (File.Exists(file))
{
using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
{
}
}
}
private static void OpenNonExistingFileWithoutCheck()
{
try
{
string file = @"C:\temp\nonexistantfile.txt";
using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
{
}
}
catch (Exception ex)
{
}
}
}
File.TryOpenRead
而不是File.OpenRead
。不幸的是,File.TryOpenRead
实际上并不存在。而且,File.Exists
与File.TryOpenRead
根本不等价。 - Ben Voigt我不确定效率如何,但我更喜欢使用File.Exists检查。问题在于其他可能发生的事情:坏文件句柄等。如果您的程序逻辑知道有时文件不存在,并且您想要对现有和不存在的文件具有不同的行为,请使用File.Exists。如果它的缺失与其他文件相关异常相同,只需使用异常处理。
File.Exists
是 File.TryOpenRead
的良好替代品。它们并不相同,不能解决问题,请不要使用它。 - Ben Voigt