C# 使用异常通知委托未设置-设计错误?

C# 使用异常通知委托未设置-设计错误?,c#,exception,delegates,class-design,C#,Exception,Delegates,Class Design,我创建了一个文档类,可以下载和读取其中的文本。聪明的是,它只在需要时下载和读取文档中的文本。通过使用Text属性,它将尝试读取文档,如果文档尚未下载,它将下载文档,然后读取 非常好。然而,我注意到我对异常的使用导致了一些古怪的代码。见下文 文档类 public delegate byte[] DownloadBinaryDelegate(IDocument document); public delegate string TextReaderDelegate(IDocument documen

我创建了一个文档类,可以下载和读取其中的文本。聪明的是,它只在需要时下载和读取文档中的文本。通过使用Text属性,它将尝试读取文档,如果文档尚未下载,它将下载文档,然后读取

非常好。然而,我注意到我对异常的使用导致了一些古怪的代码。见下文

文档类

public delegate byte[] DownloadBinaryDelegate(IDocument document);
public delegate string TextReaderDelegate(IDocument document);

public class Document
{
        public DownloadBinaryDelegate DownloadBinaryDelegate { private get; set; }
        public TextReaderDelegate TextReaderDelegate { private get; set; }

        private bool _binaryIsSet;
        private byte[] _binary;
        public byte[] Binary
        {
            get 
            {
                if (_binaryIsSet)
                    return _binary;

                if (DownloadBinaryDelegate == null)
                    throw new NullReferenceException("No delegate attached to DownloadBinaryDelegate.");

                Binary = DownloadBinaryDelegate(this);
                DownloadBinaryDelegate = null; // unhock delegate as it's no longer needed.

                return _binary;
            }
            set
            {
                if (_binaryIsSet) 
                    return;

                _binary = value;
                _binaryIsSet = true;
            }
        }

        private bool _textIsSet;
        private string _text;
        public string Text
        {
            get
            {
                if (_textIsSet)
                    return _text;

                if (TextReaderDelegate == null)
                    throw new NullReferenceException("No delegate attached to TextReaderDelegate.");

                Text = TextReaderDelegate(this); // this delegate will call Binary and return the translated text.
                TextReaderDelegate = null; // unhock delegate as it's no longer needed.

                return _text;
            }
            set
            {
                if (_textIsSet)
                    return;

                _text = value;
                _textIsSet = true;
            }
        }
问题

我一开始写的东西

if (document.Text == null) // text is not set
{
    if (document.Binary == null) // binary has not been downloaded
        document.DownloadBinaryDelegate = Util.DownloadDocument;

    document.TextReaderDelegate = Util.ReadDocument;
}
完全忘记了Text属性会引发异常。 所以我必须写这样的东西,这是一个有点古怪的代码

// check if text has already been read and set
try
{
    var isTextSet = document.Text == null;
}
catch (NullReferenceException)
{
    document.DownloadBinaryDelegate = Util.DownloadDocument;
    document.TextReaderDelegate = Util.ReadDocument;
}
我希望你能明白我的意思


所以我的问题是,这是一个糟糕的设计吗?你会怎么做?请记住,我仍然希望使用当前的功能。

惰性初始化已经烘焙到.NET framework中。我建议您使用重新实现您的类


为了回答您的特定问题,听起来您的类总是需要二进制和文本委托,因此我会将它们作为构造函数的必需参数。

我无法使用Lazy,因为我使用的是委托(this),这是不允许的

因此,我最终使用了下面这个很好的答案:


您不应该从属性中抛出异常。检查答案:从类似的问题。哈哈,我感觉有什么不对劲。我会读一读的,谢谢。我找到了一个可以接受的答案。属于codereview.stackexchange.Com不知道那件事。下一次我会知道的。这门课比所展示的内容还多,我只选了基本部分:)。我来看看Lazy@Sn在这种情况下,您的类可能违反了单一责任原则。也许值得将这个类的这一部分划分成一个新的类。我很确定它符合单一责任原则,因为它与文档有关。它是文件名和其他特定于域的文档元数据。:)我也不能通过ctor设置它,因为创建文档的地方不知道如何下载它。@Snæbjørn看,对我来说,这听起来像是违反了规定。下载文档和存储文档的元数据是两个完全不同的职责。我希望能够将下载代码与文档测试完全分开进行单元测试。但最终,这只是一个指导原则——没有什么可以让人失眠的;)
public class Document
{
    private DownloadBinaryDelegate _downloadBinaryDelegate;
    public void SetDownloadBinaryDelegate(DownloadBinaryDelegate downloadBinary)
    {
        if (downloadBinary == null)
            throw new ArgumentNullException("downloadBinary");

        _downloadBinaryDelegate = downloadBinary;
    }

    private TextReaderDelegate _textReaderDelegate;
    public void SetTextReaderDelegate(TextReaderDelegate readerDelegate)
    {
        if (readerDelegate == null)
            throw new ArgumentNullException("readerDelegate");

        _textReaderDelegate = readerDelegate;
    }

    private bool _binaryIsSet;
    private byte[] _bytes;

    public void SetBinary(byte[] bytes, bool forceOverwrite = false)
    {
        if (_binaryIsSet && !forceOverwrite)
            return;

        _bytes = bytes;
        _binaryIsSet = true;
    }

    public byte[] GetBinary()
    {
        if (_binaryIsSet)
            return _bytes;

        if (_downloadBinaryDelegate == null)
            throw new InvalidOperationException("No delegate attached to DownloadBinaryDelegate. Use SetDownloadBinaryDelegate.");

        SetBinary(_downloadBinaryDelegate(this));
        _downloadBinaryDelegate = null; // unhock delegate as it's no longer needed.

        return _bytes;
    }

    public bool TryGetBinary(out byte[] bytes)
    {
        if (_binaryIsSet)
        {
            bytes = _bytes;
            return true;
        }

        if (_downloadBinaryDelegate != null)
        {
            bytes = GetBinary(); // is this legit?
            return true;
        }

        bytes = null;
        return false;
    }

    private bool _textIsSet;
    private string _text;

    public void SetText(string text, bool forceOverwrite = false)
    {
        if (_textIsSet && !forceOverwrite)
            return;

        _text = text;
        _textIsSet = true;
    }

    public string GetText()
    {
        if (_textIsSet)
            return _text;

        if (_textReaderDelegate == null)
            throw new InvalidOperationException("No delegate attached to TextReaderDelegate. Use SetTextReaderDelegate.");

        SetText(_textReaderDelegate(this)); // this delegate will call Binary and return the read text.
        _textReaderDelegate = null; // unhock delegate as it's no longer needed.

        return _text;
    }

    public bool TryGetText(out string text)
    {
        byte[] bytes;
        if (!TryGetBinary(out bytes))
        {
            text = null;
            return false;
        }

        if (_textIsSet)
        {
            text = _text;
            return true;
        }

        if (_textReaderDelegate != null)
        {
            text = GetText(); // is this legit?
            return true;
        }

        text = null;
        return false;
    }
}