C # Дизайн по контракту: как гарантировать метод и функцию - PullRequest
2 голосов
/ 26 мая 2011

Я недавно начал использовать дизайн по контракту с моим проектом.У меня есть несколько вопросов относительно того, как гарантировать метод и функцию.

public static void SaveAttachment(Outlook.MailItem _mailItem, string saveTo)
    {
        Contract.Requires<ArgumentNullException>(_mailItem != null);
        Contract.Requires(_mailItem.Attachments.Count > 0);
        Contract.Requires(!String.IsNullOrEmpty(saveTo));            

        string attachLink = "<file://{0}>";
        StringBuilder sBuilder = new StringBuilder();
        try
        {
            foreach (string fp in saveAllEmailAttachmentsThenReturnSavedFilePathList(emailAttachmentList(_mailItem), saveTo))
            {
                sBuilder.Append(String.Format("Attachment saved: " + attachLink, fp) + Environment.NewLine);
            }
            _mailItem.Body = sBuilder.ToString() + _mailItem.Body;
            _mailItem.Save();                
        }
        catch (Exception ex)
        {
            LogErrorMessage(ex);
        }            
    }

Как вы можете видеть, вышеописанный метод сохранит вложение электронной почты и убедится, что передаваемый почтовый элемент не является нулевым и имеет вложение,Тем не менее, как я могу гарантировать, что этот метод будет делать то, что он должен делать.

Вкл. Функция:

public static List<Outlook.MailItem> SelectedMail(Outlook.Selection selectedItems)
    {
        Contract.Requires(selectedItems.Count > 0);
        Contract.Ensures(Contract.Result<List<Outlook.MailItem>>().Count > 0);

        List<Outlook.MailItem> selectedMails = new List<Outlook.MailItem>();
        foreach (object obj in selectedItems)
        {
            if (obj is Outlook.MailItem)
            {
                selectedMails.Add((Outlook.MailItem)obj);
            }
        }

        Contract.Assume(selectedMails.Count > 0);
        return selectedMails;
    }

Что если у выбранных элементов нет почтового элемента?если я напишу договор, это будет гарантировать, что это вызовет ошибку на выбранном электронном письме, к которому нет прикрепленных файлов.

Извините, я новичок в этой парадигме.Я хочу научиться этому, поэтому мне не нужно писать безопасный защитный код с помощью if-else.

Ответы [ 3 ]

2 голосов
/ 26 мая 2011

В вашем конкретном случае я вижу две возможности:

  1. Требование Ensures является ложным, и оно действительно для результата 0 Счет.*

    У вас есть дополнительное требование времени выполнения, которое не может быть разумно выражено в качестве Контракта, поскольку статическая проверка может не доказать это, и вы не захотите утверждать это.Это тот случай, когда вы, вероятно, просто хотите проверить условие во время выполнения и выдать исключение.

Если вы хотите выразить очень сложное требование в рамках контракта (илидаже для исключения), вы можете пересмотреть свой дизайн и почему существует это сложное требование.Смогут ли пользователи вашего метода запомнить и понять требования?Есть ли какой-то другой способ, которым вы можете инкапсулировать поведение, чтобы требование было выражено более четко?

1 голос
/ 26 мая 2011

Ваш код с .Assume(), вероятно, не будет выдавать предупреждения.

Но вам нужен Assume(), отговорка, потому что статический верификатор не может достаточно глубоко заглянуть в источник ваших данных. Предположим, что вы берете на себя ответственность, вставляя часть «внешней» информации.

Пока вы поддерживаете .Requires, это нормально. Статическая проверка проверяет остальную часть вашей логики, предполагая, что есть действительные данные.

Но учтите, что на практике я бы сделал это немного по-другому:

    public static List<Outlook.MailItem> SelectedMail(Outlook.Selection selectedItems)
    {
        Contract.Requires(selectedItems != null);    //always a good idea
        Contract.Requires(selectedItems.Count > 0);

        // can't promise this
        // Contract.Ensures(Contract.Result<List<Outlook.MailItem>>().Count > 0);  

        List<Outlook.MailItem> selectedMails = new List<Outlook.MailItem>();
        foreach (object obj in selectedItems)
        {
            if (obj is Outlook.MailItem)
            {
                selectedMails.Add((Outlook.MailItem)obj);
            }
        }

        // and then w/o the promise we don't need this
        // Contract.Assume(selectedMails.Count > 0);
        return selectedMails;
    }


    void SomeMethod()
    {
       var selected = SelectedMail(...);
       if (selected.Count > 0)
          SaveAttachment(selected, "file")
       else
          // you need a plan here anyway

    }

Это подтвердит без Assume ()

1 голос
/ 26 мая 2011

Ну, для второго фрагмента кода вы можете попробовать:

Contract.Requires(Contract.Exists(selectedItems, x => x is Outlook.MailItem));

РЕДАКТИРОВАТЬ: Gah - если Outlook.Selection реализует IEnumerable, но не IEnumerable<T>, это не будет работать: (

Я не уверен, насколько хорошо такой контракт может быть проверен статическим компилятором, правда ...

Кстати, размер вашего метода можно очень легко уменьшить:

return selectedItems.OfType<Outlook.MailItem>().ToList();

Разве LINQ не прекрасен? :)

Я не совсем уверен, что вы пытаетесь гарантировать в первом куске кода ... Я не думаю, что вы действительно можете выразить часть "это спасено", если честно.

...