我应该对C#调用的F#代码进行空保护吗? [英] Should I null-protect my F# code from C# calls

查看:85
本文介绍了我应该对C#调用的F#代码进行空保护吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用F#写一个库,其中包含一些公开可见的接口和基类.通常,我避免在自定义类型上指定[<AllowNullLiteral>],因为这会使我的F#代码中的验证逻辑复杂化(请参阅

I am writing a library in F#, with some interfaces and base classes that are publicly visible. Generally, I avoid specifying [<AllowNullLiteral>] on my custom types as this complicates the validation logic in my F# code (see this nice post for goods and bads of null handing in F# to get a picture ), and also, F# does not initially allow null for F# types. So, I validate for nulls only for types that accept the null value as valid.

但是,当我的库从另一种.NET语言(例如C#)中使用时,会出现问题.更具体地说,当由C#代码调用时,我担心如何实现接受F#声明的接口的方法.接口类型在C#中可以为空,并且我怀疑C#代码将null传递给我的F#方法不会有问题.

However, an issues arises when my library is used from another .NET language, such as C#. More particularly, I worry how should I implement methods that accept F#-declared interfaces, when called by C# code. The interface types are nullable in C#, and I suspect that there will not be an issue for the C# code to pass a null to my F# method.

我担心调用者会崩溃并用NPE烧毁,问题是我什至不被允许在F#代码中正确处理它-比如抛出ArgumentNullException-因为相应的接口缺少AllowNullLiteral属性.我担心我将不得不使用该属性并在F#代码中添加相关的null检查逻辑,以最终防止此类灾难.

I fear that the caller would crash and burn with a NPE, and the problem is that I am not even allowed to properly handle that in the F# code -- say throw an ArgumentNullException -- because the respective interface lack the AllowNullLiteral attribute. I fear that I would have to use the attribute and add the related null-checking logic in my F# code to eventually prevent such a disaster.

我的担心合理吗?最初尝试遵循良好的F#惯例并尽可能避免使用null时,我有些困惑.如果我的目标之一是允许C#代码继承并实现我在F#中创建的接口,那么这将如何改变?如果我的F#代码中的所有非值类型都是公共的,并且可以从任何CLR语言访问,我是否必须允许它们为空值?是否有最佳实践或好的建议可以遵循?

Are my fears reasonable? I am a little confused as I initially attempt to stick to the good F# practices and avoid null as much as possible. How does this change if among my goals is to allow C# code to subclass and implement the interfaces I created in F#? Do I have to allow nulls for all non-value types coming from my F# code if they are public and can be accessed from any CLR language? Is there a best practice or a good advice to follow?

推荐答案

您可以采用两种基本方法:

There are two basic approaches you can take:

  1. API设计中的文档,不允许将null传递到您的库,并且调用代码负责确保您的库永远不会收到null.然后忽略该问题,当您的代码引发NullReferenceException且用户对此抱怨时,请将其指向文档.

  1. Document in your API design that passing null to your library is not allowed, and that the calling code is responsible for ensuring that your library never receives a null. Then ignore the problem, and when your code throws NullReferenceExceptions and users complain about it, point them to the documentation.

假定您的库从外部"接收的输入不可信,并在库的外部"边缘周围放置一个验证层.该验证层将负责检查null并抛出ArgumentNullException. (并指向在异常消息中显示不允许使用空值"的文档).

Assume that the input your library receives from "outside" cannot be trusted, and put a validation layer around the "outside-facing" edge of your library. That validation layer would be responsible for checking for null and throwing ArgumentNullExceptions. (And pointing to the documentation that says "No nulls allowed" in the exception message).

您可能会猜到,尽管需要花费更多时间,但我还是偏爱方法2.但是通常您可以创建一个函数,以供您使用:

As you can probably guess, I favor approach #2, even though it takes more time. But you can usually make a single function, used everywhere, to do that for you:

let nullArg name message =
    raise new System.ArgumentNullException(name, message)

let guardAgainstNull value name =
    if isNull value then nullArg name "Nulls not allowed in Foo library functions"

let libraryFunc a b c =
    guardAgainstNull a nameof(a)
    guardAgainstNull b nameof(b)
    guardAgainstNull c nameof(c)
    // Do your function's work here

或者,如果您具有更复杂的数据结构,必须检查内部null,则将其视为HTML表单中的验证问题.您的验证函数将抛出异常,否则将返回有效的数据结构.因此,库的其余部分可以完全忽略空值,并以一种很好的,简单的,惯用的F#方式编写.验证功能可以处理域功能和不受信任的外部世界"之间的接口,就像使用HTML格式的用户输入一样.

Or, if you have a more complicated data structure that you have to inspect for internal nulls, then treat it like a validation problem in HTML forms. Your validation functions will either throw an exception, or else they will return valid data structures. So the rest of your library can ignore nulls completely, and be written in a nice, simple, idiomatic-F# way. And your validation functions can handle the interface between your domain functions and the untrusted "outside world", just as you would with user input in an HTML form.

更新:另请参见 https底部附近给出的建议://fsharpforfunandprofit.com/posts/the-option-type/(在"F#和null"部分中),Scott Wlaschin写道:一般而言,永远不会在纯" F#中创建null ,但只能通过与.NET库或其他外部系统进行交互.在这种情况下,最好立即检查空值并将其转换为选项类型!"您的库代码(希望从其他.NET库获取数据)将处于类似情况.如果要允许空值,则将它们转换为Option类型的None值.如果您想禁止它们并在传递空值时抛出ArgumentNullException,那么您也可以在库的边界上这样做.

Update: See also the advice given near the bottom of https://fsharpforfunandprofit.com/posts/the-option-type/ (in the "F# and null" section), where Scott Wlaschin writes, "As a general rule, nulls are never created in "pure" F#, but only by interacting with the .NET libraries or other external systems. [...] In these cases, it is good practice to immediately check for nulls and convert them into an option type!" Your library code, which expects to get data from other .NET libraries, would be in a similar situation. If you want to allow nulls, you'd convert them to the None value of an Option type. If you want to disallow them and throw ArgumentNullExceptions when you get passed a null, you'd also do that at the boundaries of your library.

这篇关于我应该对C#调用的F#代码进行空保护吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆