C#契约式设计:如何保证方法和功能

发布于 2024-11-09 10:56:13 字数 1841 浏览 6 评论 0原文

我最近开始在我的项目中使用合同设计。我有一些关于如何保证方法和功能的问题。

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);
        }            
    }

正如您所看到的,上述方法将保存电子邮件附件,并确保传入的邮件项目不为空并且具有附件。然而,我如何保证这个方法会做它应该做的事情。

On Function:

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;
    }

如果 selectedItems 没有 mailitem 怎么办?如果我以这种方式编写合同确保会导致选定的没有附件的电子邮件出现错误。

抱歉,我是这个范例的新手。我想学习它,这样我就不必用 if-else 编写安全防护代码。

I recently started using design by contract with my project. I have some questions regarding on how to guarantee method and function.

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);
        }            
    }

As you can see the above method will save email attachment and it make sure the mail item that is pass in is not null and have attachment. However, how do i guarantee that this method will do what it suppose to do.

On Function:

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;
    }

What if the selectedItems have no mailitem? if I write contract ensures this way would it cause error on selected email that have no attachment.

Sorry, I'm new to this paradigm. I want to learn it so I don't have to write safe guard code with if-else.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

木有鱼丸 2024-11-16 10:56:13

对于您的具体情况,我看到两种可能性:

  1. Ensures 要求为 false,且对于 0 Count 结果有效。

    Ensures

  2. 您有一个额外的运行时要求,无法合理地表达为合同,因为它不是静态检查器希望证明的东西,因此您不想断言它。在这种情况下,您可能只想在运行时检查条件并抛出异常。

如果您发现自己想要将非常复杂的需求表达为合同的一部分(甚至抛出异常),您可能需要重新考虑您的设计以及为什么存在这种复杂的需求。使用您的方法的用户能够记住并理解需求吗?是否有其他方法可以封装行为以便更清楚地表达需求?

For your specific case, I see two possibilities:

  1. The Ensures requirement is false and it's valid for there to be a 0 Count result.

  2. You have an additional runtime requirement that cannot be reasonably expressed as a Contract, since it's not something that a static checker could hope to prove and so you would not want to Assert it. This is the case where you probably just want to check the condition at runtime and throw an Exception.

If you find yourself wanting to express a very complex requirement as part of a Contract (or even to throw an Exception), you might want to reconsider your design and why this complex requirement exists. Will the users of your method be able to remember and understand the requirement? Is there some other way you can encapsulate the behavior so that the requirement is expressed more clearly?

ˉ厌 2024-11-16 10:56:13

您的带有 .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() 的情况下进行验证

Your code, with the .Assume() will probably not give warnings.

But you need an Assume() , a cop-out, because the static verifier cannot look far enough back into the source of your data. Assume is where you take responsibility by inserting a piece of 'outside' information.

As long as you keep the .Requires active this is OK. The static checker verifies the rest of your logic assuming there is valid data.

But note that in practice I would do it a little different:

    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

    }

This would verify without an Assume()

も让我眼熟你 2024-11-16 10:56:13

好吧,对于第二个代码片段,您可以尝试:

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

编辑:Gah - 如果 Outlook.Selection 实现 IEnumerable 但不是 IEnumerable,那么行不通 :(

诚然,我不确定静态编译器对这样的契约的检查效果如何......顺便说

一句,你的方法体可以很容易地减小大小:

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

LINQ 不是很可爱吗?: )

我不太确定你想在第一段代码中保证什么......老实说,我认为你无法真正表达“它被保存”部分。

Well, for the second code snippet you could try:

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

EDIT: Gah - if Outlook.Selection implements IEnumerable but not IEnumerable<T>, that won't work :(

I'm not sure how well such a contract can be checked by the static compiler, admittedly...

By the way, your method body can be reduced in size really easily:

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

Isn't LINQ lovely? :)

I'm not quite sure what you're trying to guarantee in the first piece of code... I don't think you can really express the "it gets saved" part, to be honest.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文