โดยพื้นฐานแล้วฉันต้องการตรวจสอบว่าฉันมีสิทธิ์เปิดไฟล์ก่อนที่จะลองเปิดจริงหรือไม่ ฉันไม่ต้องการใช้ try / catch สำหรับการตรวจสอบนี้เว้นแต่ฉันจะต้องทำ มีคุณสมบัติการเข้าถึงไฟล์ที่ฉันสามารถตรวจสอบก่อนส่งได้หรือไม่?
โดยพื้นฐานแล้วฉันต้องการตรวจสอบว่าฉันมีสิทธิ์เปิดไฟล์ก่อนที่จะลองเปิดจริงหรือไม่ ฉันไม่ต้องการใช้ try / catch สำหรับการตรวจสอบนี้เว้นแต่ฉันจะต้องทำ มีคุณสมบัติการเข้าถึงไฟล์ที่ฉันสามารถตรวจสอบก่อนส่งได้หรือไม่?
คำตอบ:
ที่ผ่านมาฉันทำแบบนี้มาแล้วนับครั้งไม่ถ้วนและเกือบทุกครั้งที่ทำไปแล้วฉันคิดผิดที่แม้แต่จะพยายาม
สิทธิ์ของแฟ้ม (แม้การดำรงอยู่ของไฟล์) เป็นสารระเหย - พวกเขาสามารถเปลี่ยนแปลงได้ตลอดเวลา ต้องขอบคุณกฎหมายของ Murphy โดยเฉพาะอย่างยิ่งรวมถึงช่วงเวลาสั้น ๆ ระหว่างเมื่อคุณตรวจสอบไฟล์และเมื่อคุณพยายามเปิด การเปลี่ยนแปลงมีโอกาสมากขึ้นหากคุณอยู่ในพื้นที่ที่คุณรู้ว่าต้องตรวจสอบก่อน มันแปลกมากพอที่จะไม่เกิดขึ้นในสภาพแวดล้อมการทดสอบหรือการพัฒนาของคุณซึ่งมักจะค่อนข้างคงที่ สิ่งนี้ทำให้ปัญหายากต่อการติดตามในภายหลังและทำให้ข้อบกพร่องประเภทนี้นำไปสู่การผลิตได้ง่าย
สิ่งนี้หมายความว่าคุณยังต้องสามารถจัดการกับข้อยกเว้นได้หากการอนุญาตหรือการมีอยู่ของไฟล์ไม่ดีแม้ว่าคุณจะตรวจสอบแล้วก็ตาม จำเป็นต้องมีรหัสการจัดการข้อยกเว้นไม่ว่าคุณจะตรวจสอบสิทธิ์ของไฟล์ล่วงหน้าหรือไม่ รหัสการจัดการข้อยกเว้นจัดเตรียมฟังก์ชันการทำงานทั้งหมดของการมีอยู่หรือการตรวจสอบสิทธิ์ นอกจากนี้ในขณะที่ตัวจัดการข้อยกเว้นเช่นนี้เป็นที่ทราบกันดีว่าช้า แต่สิ่งสำคัญคือต้องจำไว้ว่าดิสก์ i / o นั้นช้ากว่า ... ช้ากว่ามาก ... และการเรียกใช้ฟังก์ชัน. Exists () หรือการตรวจสอบสิทธิ์จะบังคับให้มีการเดินทางเพิ่มเติม ออกจากระบบไฟล์
โดยสรุปแล้วการตรวจสอบเบื้องต้นก่อนที่จะพยายามเปิดไฟล์นั้นทั้งซ้ำซ้อนและสิ้นเปลือง ไม่มีประโยชน์เพิ่มเติมใด ๆ นอกเหนือจากการจัดการข้อยกเว้นมันจะทำร้ายจริงไม่ได้ช่วยประสิทธิภาพของคุณมันเพิ่มต้นทุนในแง่ของโค้ดที่ต้องดูแลมากขึ้นและมันสามารถนำบั๊กที่ละเอียดอ่อนมาสู่โค้ดของคุณได้ ไม่มีข้อเสียใด ๆ เลยในการตรวจสอบเบื้องต้น แต่สิ่งที่ถูกต้องในที่นี้คือเพียงพยายามเปิดไฟล์และใช้ความพยายามของคุณในการจัดการข้อยกเว้นที่ดีหากล้มเหลว เช่นเดียวกันแม้ว่าคุณจะเพิ่งตรวจสอบว่ามีไฟล์อยู่หรือไม่ก็ตาม เหตุผลนี้นำไปใช้ใด ๆทรัพยากรระเหย
เคล็ดลับง่ายๆสำหรับใครก็ตามที่มาที่นี่พร้อมกับปัญหาที่คล้ายกัน:
ระวังแอปการซิงโครไนซ์เว็บเช่น DropBox ฉันเพิ่งใช้เวลา 2 ชั่วโมงในการคิดว่าคำสั่ง "ใช้" (รูปแบบการทิ้ง) ใช้งานไม่ได้ใน. NET
ในที่สุดฉันก็รู้ว่า Dropbox กำลังอ่านและเขียนไฟล์อยู่เบื้องหลังอย่างต่อเนื่องเพื่อที่จะซิงค์ไฟล์เหล่านี้
เดาว่าโฟลเดอร์ Visual Studio Projects ของฉันอยู่ที่ไหน? ภายในโฟลเดอร์ "My Dropbox" แน่นอน
ดังนั้นเมื่อฉันรันแอปพลิเคชันของฉันในโหมด Debug ไฟล์ที่กำลังอ่านและเขียนก็ยังถูกเข้าถึงอย่างต่อเนื่องโดย DropBox เพื่อซิงค์กับเซิร์ฟเวอร์ DropBox สิ่งนี้ทำให้เกิดความขัดแย้งในการล็อก / การเข้าถึง
อย่างน้อยตอนนี้ฉันก็รู้แล้วว่าฉันต้องการฟังก์ชั่น File Open ที่มีประสิทธิภาพมากขึ้น (เช่น TryOpen () ที่จะพยายามหลายครั้ง) ฉันแปลกใจที่มันไม่ได้เป็นส่วนหนึ่งของเฟรมเวิร์กในตัว
[อัปเดต]
นี่คือฟังก์ชั่นตัวช่วยของฉัน:
/// <summary>
/// Tries to open a file, with a user defined number of attempt and Sleep delay between attempts.
/// </summary>
/// <param name="filePath">The full file path to be opened</param>
/// <param name="fileMode">Required file mode enum value(see MSDN documentation)</param>
/// <param name="fileAccess">Required file access enum value(see MSDN documentation)</param>
/// <param name="fileShare">Required file share enum value(see MSDN documentation)</param>
/// <param name="maximumAttempts">The total number of attempts to make (multiply by attemptWaitMS for the maximum time the function with Try opening the file)</param>
/// <param name="attemptWaitMS">The delay in Milliseconds between each attempt.</param>
/// <returns>A valid FileStream object for the opened file, or null if the File could not be opened after the required attempts</returns>
public FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess,FileShare fileShare,int maximumAttempts,int attemptWaitMS)
{
FileStream fs = null;
int attempts = 0;
// Loop allow multiple attempts
while (true)
{
try
{
fs = File.Open(filePath, fileMode, fileAccess, fileShare);
//If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
break;
}
catch (IOException ioEx)
{
// IOExcception is thrown if the file is in use by another process.
// Check the numbere of attempts to ensure no infinite loop
attempts++;
if (attempts > maximumAttempts)
{
// Too many attempts,cannot Open File, break and return null
fs = null;
break;
}
else
{
// Sleep before making another attempt
Thread.Sleep(attemptWaitMS);
}
}
}
// Reutn the filestream, may be valid or null
return fs;
}
using
จะต้องใช้แม้ว่า ...
using
จะไม่ทำงานที่นี่ ในตอนท้ายของบล็อกการใช้งานfs
จะบังคับปิด คุณจะให้ผู้โทรสตรีมไฟล์แบบปิด (ไร้ประโยชน์)!
นี่คือทางออกที่คุณกำลังมองหา
var fileIOPermission = new FileIOPermission(FileIOPermissionAccess.Read,
System.Security.AccessControl.AccessControlActions.View,
MyPath);
if (fileIOPermission.AllFiles == FileIOPermissionAccess.Read)
{
// Do your thing here...
}
สิ่งนี้จะสร้างสิทธิ์การอ่านใหม่ตามมุมมองสำหรับเส้นทางของไฟล์ทั้งหมดจากนั้นตรวจสอบว่าเท่ากับการเข้าถึงไฟล์หรือไม่
ประการแรกสิ่งที่ Joel Coehoorn พูด
นอกจากนี้คุณควรตรวจสอบสมมติฐานที่ว่าคุณต้องการหลีกเลี่ยงการใช้ try / catch น้อยที่สุดเว้นแต่คุณจะต้องทำ เหตุผลทั่วไปในการหลีกเลี่ยงตรรกะที่ขึ้นอยู่กับข้อยกเว้น (การสร้างException
วัตถุทำงานได้ไม่ดี) อาจไม่เกี่ยวข้องกับโค้ดที่เปิดไฟล์
ฉันคิดว่าหากคุณกำลังเขียนวิธีการเติมข้อมูลList<FileStream>
โดยการเปิดทุกไฟล์ในแผนผังย่อยของไดเรกทอรีและคุณคาดว่าไฟล์จำนวนมากจะไม่สามารถเข้าถึงได้คุณอาจต้องการตรวจสอบสิทธิ์ของไฟล์ก่อนที่จะพยายามเปิดไฟล์เพื่อที่คุณจะไม่ได้ ได้รับข้อยกเว้นมากเกินไป แต่คุณยังคงจัดการกับข้อยกเว้น นอกจากนี้อาจมีบางอย่างผิดปกติอย่างมากกับการออกแบบโปรแกรมของคุณหากคุณกำลังเขียนวิธีการที่ทำเช่นนี้
public static bool IsFileLocked(string filename)
{
bool Locked = false;
try
{
FileStream fs =
File.Open(filename, FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);
fs.Close();
}
catch (IOException ex)
{
Locked = true;
}
return Locked;
}
public static FileStream GetFileStream(String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, ref int attempts, int attemptWaitInMilliseconds)
{
try
{
return File.Open(filePath, fileMode, fileAccess, fileShare);
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
if (attempts <= 0)
{
throw unauthorizedAccessException;
}
else
{
Thread.Sleep(attemptWaitInMilliseconds);
attempts--;
return GetFileStream(filePath, fileMode, fileAccess, fileShare, ref attempts, attemptWaitInMilliseconds);
}
}
}
attempts
ผ่านการอ้างอิง นั่นไม่สมเหตุสมผล ไม่ไม่การทดสอบแทนเพียง<=
==
throw ex
เป็นสิ่งที่ถูกต้องที่จะทำ