强制方法不返回空值



正如 8 年前在这里提出的问题,但我认为应该有一种方法(新模式、新设计、新架构或其他任何东西)来强制执行方法不返回 null。

如您所知,在对我来说很重要的方法中返回 null 有一些含义:

在消费端和可理解语义中处理 null,例如:

方法:

public ClassName Do()
{
...
return null;
}

并称Do()为(注意评论也):

var objVal = Do(); 
//Accessing property of ClassName raised exception
var pnVal = objVal.PropName;//Exception id objVal is null
//But I should handle if it is not null then do anything I want
if(objVal!= null)
{
//Do something
}

在通过上述方式对产品产生许多问题之后,我得出了这个结论,即概括所有方法,以遵循可读,干净并防止歧义语义的模式。

所以一个非常基本的方法是使用Struct类型,因为结构不能为空,如果方法的返回类型是结构的,那么它们不能返回 null,我们知道这是在编译时而不是在运行时

。所以我实现了上面的方法,例如:

1-使DTOoutin方法,在这种情况下只需out

public struct Do_DTO_Out
{
public ClassName Prop1 { get; set; }
public bool IsEmpty
{
get
{
return Prop1 == null;
}
}
public static Do_DTO_Out Empty
{
get
{
return new Do_DTO_Out() { Prop1 = null };
}
}
}

2-Do方法应为:

public Do_DTO_Out Do()
{
try
{
return manipulatedObj;
}
catch (Exception exp)
{
}
return Do_DTO_Out.Empty;
}

3-在消费方面:

var objVal = Do();
if (!objVal.IsEmpty)
//Do something

struct是最好的方法吗? 是否值得更改所有方法并为每个方法创建 DTOinout(我认为是这样)。

有没有更好的方法可以做到这一点,任何想法,帮助,答案将不胜感激。

您的"引用类型"到"带有属性检查的结构"的转换对我来说似乎毫无用处。它还需要深入了解您的意图,而引用类型 null 检查对于以后阅读它的任何人来说都非常明显。

我认为代码合同可以为您工作。它为您提供编译时静态分析和运行时检查。只要确保您有适当的合同作为发布条件:

public ClassName Do()
{
...
object returnValue = null;
Contract.Ensures(returnValue != null);
return returnValue;
}

假设该值永远无法null否则if是不可避免的(但对于单个方法调用,您现在可以编写Do()?.DoSomething())。

如果你可以引入代码契约(参见 Patrick 的回答),那么我完全同意 Patrick 的观点,你应该接受它们。如果它不可行(因为你的代码库已经太大了,或者你的目标是一个不支持它们的环境),那么我会首先使用断言:

var obj = Do();
Debug.Assert(obj != null);
// Use obj...

但是,我们将此责任转移到呼叫点,这可能很乏味。如果你想使这个接口显式,那么你可以按照你的想法使用一些结构体,但在调用点(错误所在)抛出异常:

public NotNullable<SomeClass> Do() { }

其中NotNullable<T>定义为:

public struct NotNullable<T> where T : class
{
public NotNullable(T value)
{
Value = value ?? throw new ArgumentNullException(nameof(value));
}
public T Value { get; }
}

但是我不喜欢在调用点显式访问.Value,然后我会使其透明,并添加:

public static implicit operator T(NotNullable<T> rhs)
=> rhs.Value;

现在调用者可以是:

MyClass obj = Do();
obj.DoSomthing();

创建对象时会抛出正确的异常(不幸的是,在运行时)。使用[Conditional("DEBUG")],您可以排除对具有类似于Debug.Assert()的行为和最小(但仍然存在)开销的发布版本的检查。

请注意,仅当您要直接在其签名中记录有关此约束的接口方法时,这才有意义。如果您对此不感兴趣,请尽可能保持简单:

public SomeClass Do()
{
MyClass somevalue = ...
// ...
return NotNull(somevalue);
}

其中NotNull()是一个静态方法,在某处定义并使用using static导入,甚至是object的扩展方法,称为return somevalue.NotNull()

我不是特别喜欢这种方法,因为我认为在这些情况下Debug.Assert()就足够了,但这只是我的意见。当然,也许有一天我们会在 C# 中拥有可为空的引用类型,然后我们将获得编译时强制(如object?或相反的方式object!)。

返回 null 是一种不好的做法 - 最好实现空对象设计模式

相关内容

  • 没有找到相关文章

最新更新