#c# #.net #idisposable
#c# #.net #idisposable
Вопрос:
У меня есть следующие два конструктора:
public Source(FileStream fileStream) {
// Stuff
}
public Source(String fileName) : this(new FileStream(fileName, FileMode.Open)) {
// Nothing, the other constructor does the work
}
Проблема со вторым конструктором довольно очевидна, FileStream создается и используется, но не удаляется. Поскольку он находится внутри цепочки конструкторов, using
блок невозможен. Я не могу переместить new FileStream()
в тело конструктора, потому что, хотя он тогда будет находиться в using
блоке, логика другого конструктора не сможет быть вызвана. Я не могу извлечь эту логику, потому что она изменяет readonly
поля. Я мог бы дублировать логику в каждом конструкторе, но это, очевидно, не очень хорошее решение.
Я действительно предпочел бы сохранить синтаксический сахар, предоставляемый вторым конструктором. Как я могу лучше всего это сделать? Или это просто плохая идея?
Ответ №1:
Взгляните на StreamReader
реализацию, которая имеет два типа ctors:
public StreamReader(Stream stream)
: this(stream, true)
{
}
public StreamReader(string path)
: this(path, true)
{
}
Внутренне они оба вызывают один и тот же Init
метод с параметром leaveOpen
, который устанавливается true
равным для первого ctor и false
для второго ctor, и на основе этого параметра Stream
выполняется (или нет) удаление.
Итак, вы можете сделать что-то вроде этого:
public class Source : IDisposable
{
private readonly Stream _stream;
private readonly bool _leaveOpen;
private Source(Stream stream, bool leaveOpen)
{
_stream = stream;
_leaveOpen = leaveOpen;
}
public Source(FileStream fileStream) : this(fileStream, true)
{
}
public Source(string fileName) : this(new FileStream(fileName, FileMode.Open), false)
{
}
public void Dispose()
{
if (!_leaveOpen)
{
_stream?.Dispose();
}
}
}
Комментарии:
1. Хотя это правильно, это намного сложнее для того, что, я думаю, хочет OP (поскольку им, похоже, не нужно обращаться к файловым прошлым конструкторам)
Ответ №2:
Я не совсем уверен, что мешает вам избавиться от него в конструкторе, который принимает FileStream
:
public Source(FileStream fileStream) {
try
{
// Stuff
}
finally
{
fileStream.Dispose();
}
}
public Source(String fileName) : this(new FileStream(fileName, FileMode.Open)) {
// Nothing, the other constructor does the work
}
Если это связано с тем, что вы хотите Stream
сохранить вызов для вызывающих FileStream
конструктор, вы можете добавить третий private
конструктор:
public Source(FileStream fileStream): this(fileStream, disposeStream: false) {
// Nothing, the other constructor does the work
}
public Source(String fileName) : this(new FileStream(fileName, FileMode.Open), disposeStream: true) {
// Nothing, the other constructor does the work
}
private Source(FileStream fileStream, bool disposeStream) {
try
{
// Stuff
}
finally
{
if (disposeStream)
{
fileStream.Dispose();
}
}
}
Комментарии:
1. github.com/DotNetAnalyzers/IDisposableAnalyzers/blob/master/… Я пишу библиотеку, предназначенную для использования третьими лицами. Удаление чьего-либо объекта — абсолютно ужасная вещь. Время жизни объекта — это то, чем они должны управлять, а не я для них. Это хорошо известная проблема, за которую вы выступаете.
2. @Patrick Я не уверен, что понимаю — разве мое второе предложенное решение не справляется с этим? Он удаляет ваш собственный объект, но не их. Я действительно не понимаю, как я выступаю за это.