Я пишу приложение на C# (.net 3.5), и у меня есть вопрос о дизайне классов:
Я хотел бы создать класс, который обращается к файлу (чтение, запись) и предоставляет его содержимое пользователям (создателям экземпляров) класса. Наиболее распространенной операцией с экземпляром будет получение определенного значения из файла. Фактические операции чтения и записи (io) очень дороги, поэтому я хотел бы сохранить данные файла в памяти и позволить всем экземплярам обращаться к этим данным. Класс расположен в сборке, которая используется одновременно из различных приложений, поэтому я думаю, мне следует беспокоиться о безопасности потоков.
Как это спроектировать с учетом потоковой безопасности и возможности модульного тестирования (для модульных тестов должны использоваться другие входные файлы, чем в операционном коде)? Любая помощь приветствуется.





Во-первых, сделайте так, чтобы ваш класс реализовал соответствующий интерфейс. Таким образом, клиенты могут протестировать свое поведение, вообще не нуждаясь в реальных файлах.
Тестировать безопасность потоков сложно - я никогда не видел ничего действительно полезного в этом отношении, хотя нельзя сказать, что инструментов там нет.
Для модульного тестирования вашего класса я бы посоветовал, если возможно, работать с общим потоком, а не просто с файлом. Затем вы можете встраивать разные тестовые файлы в свою тестовую сборку и ссылаться на них с помощью GetManifestResourceStream. Я делал это несколько раз в прошлом, и с большим успехом.
Ах, я не заметил, что вам нужен доступ как для записи, так и для чтения. Это мучительно с точки зрения тестирования, хотя вы можете использовать MemoryStream для его поддержки. Что касается интерфейса, проработайте то, что нужно клиентам. Все ли Get / SetPortion? Если да, то это ваш интерфейс.
Что касается безопасности потоков: безопасность потоков не является проблемой, если несколько потоков в одном приложении не будут одновременно ссылаться на один и тот же экземпляр вашего класса. Если ваш класс не содержится на внепроцессном сервере, у нескольких приложений нет возможности одновременно ссылаться на один и тот же экземпляр. Следовательно, конфликты, которые вы, вероятно, увидите, будут происходить из-за нарушений совместного использования файлов, а не из-за проблем с потоками (другими словами, разные экземпляры класса пытаются читать и записывать один и тот же файл). И да, вы должны разработать свой код таким образом, чтобы он соответствовал обмену файлами.
Один из способов сделать тестируемый модуль класса - предоставить классу поток в конструкторе вместо того, чтобы класс напрямую обращался к файлу. Затем модульный тест может предоставлять поток памяти, например, вместо предоставления потока файлов.
ad thread-security: означает ли это, что даже если я сделаю класс статическим, различные приложения не получат один и тот же статический объект? Возможность тестирования рекламы: я подумал о вашем подходе, но тогда мне нужно будет читать данные из файла каждый раз, когда создается экземпляр, чего я бы хотел избежать.
Отдельные домены приложений (или приложения) никогда не используют общие экземпляры. Единственный способ поделиться экземпляром - с сервером вне процесса.
Используйте ReaderWriterLock, что, как мне кажется, соответствует описанию проблемы.
Ниже приводится быстрая и грязная реализация. Получение блокировок может быть более разумным, например, несколько попыток перед спасением и т. д. Но вы поняли суть:
public class MyFooBarClass
{
private static ReaderWriterLock readerWriterLock = new ReaderWriterLock();
private static MemoryStream fileMemoryStream;
// other instance members here
public void MyFooBarClass()
{
if (fileMemoryStream != null)
{
// probably expensive file read here
}
// initialize instance members here
}
public byte[] ReadBytes()
{
try
{
try
{
readerWriterLock.AcquireReaderLock(1000);
//... read bytes here
return bytesRead;
}
finally
{
readerWriterLock.ReleaseReaderLock();
}
}
catch(System.ApplicationException ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public void WriteBytes(bytes[] bytesToWrite)
{
try
{
try
{
readerWriterLock.AcquireWriterLock(1000);
//... write bytes here
}
finally
{
readerWriterLock.ReleaseWriterLock();
}
}
catch(System.ApplicationException ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
Не могли бы вы предоставить более подробную информацию? Я до сих пор не знаю, как спроектировать класс. Как должен выглядеть интерфейс? Только GetFilePortionX / SetFilePortionY? Как я могу избежать повторной загрузки файловых данных при каждой установке. Потерпите меня, я начинающий и действительно зациклился на этом.