我想知道是否有必要再次验证传递给私有类方法的参数,不使用Reflection只会被同一个类中的公共方法调用。
如果私有方法实例化一个需要处理的对象(在由于错误的参数而出现问题之前),则可能抛出异常(在这种情况下,对象将被处理掉),对吗?
我正在查看.NET的一些源代码(主要是String
和Stream
类)。我发现一些私有方法参数是通过合同验证的,但在其他方面没有发生检查。
不再验证参数的代码(取自String
类)。在这种情况下,由于NullReferenceException
参数,可以抛出trimChars
。
[System.Security.SecuritySafeCritical] // auto-generated
private String TrimHelper(char[] trimChars, int trimType) {
//end will point to the first non-trimmed character on the right
//start will point to the first non-trimmed character on the Left
int end = this.Length-1;
int start=0;
//Trim specified characters.
if (trimType !=TrimTail) {
for (start=0; start < this.Length; start++) {
int i = 0;
char ch = this[start];
for( i = 0; i < trimChars.Length; i++) {
// ... more code
两次验证参数的代码(一次在公共中,一次在私有方法中)是这个(取自Stream
类)。
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public virtual Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
{
if (destination == null)
throw new ArgumentNullException("destination");
// ... more code goes here
return CopyToAsyncInternal(destination, bufferSize, cancellationToken);
}
private async Task CopyToAsyncInternal(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
{
Contract.Requires(destination != null);
// ... more code
}
哪种是最佳做法,还是取决于具体情况 - 它是一个仅在特定项目中使用的类库,还是会在事先不知道的不同环境中使用?
答案 0 :(得分:2)
我想知道是否有必要再次验证传递给私有类方法的参数,不使用Reflection只会被同一类中的公共方法调用。
这主要取决于您是否支持使用反射调用这些方法的第三方调用者。大多数人没有,也没有设计他们的代码来支持它。我也没有,也不推荐它。
重命名private
方法时,您是否担心会破坏使用反射的第三方来电者?如果没有,那么也不要担心这些方法中的代码。
但请参见下文。
如果私有方法实例化一个需要处理的对象(在由于错误的参数而出现问题之前),则可能抛出异常(在这种情况下,对象将被处理掉),对吗?
没有。 Dispose()
是许多类实现的方法,通常与IDisposable
接口一起实现,但对于运行时,此接口和方法都没有任何特定含义。
如果一个对象被实例化,并且没有对该对象的引用,它将(在某些时候)被垃圾收集,此时类的析构函数(也就是终结器)运行。没有规则说Dispose()
方法和析构函数必须做同样的事情,并且有时候有很好的理由说明为什么他们不做同样的事情。但是,如果您有任何非托管资源,那么几乎可以肯定,两者都会释放这些资源。
哪种是最佳做法,还是取决于具体情况 - 是否只是在特定项目中使用的类库,或者是否会在事先无法知晓的不同环境中使用?
嗯, 在private
方法中进行合同验证有意义的一个原因是,如果你移动{{1>的合同验证 方法。
鉴于
public
将合同验证放在public void Foo(string x) { Baz(x, false); }
public void Bar(string x) { Baz(x, true); }
private void Baz(string x, bool y) {
if (x == null) throw new ArgumentNullException("x");
// ...
}
中可能有意义,以减少代码重复。
另一次将合同验证放在Baz
方法中是有意义的,如果你有外部工具进行静态合同检查(主要是代码合同),合同检查员无法验证你的方法的主体没有违反任何其他方法的合同:
private
如果合同检查程序发现private void Foo(string x) { Bar(x); }
要求Bar
非空,并且无法验证对x
的所有调用是否都会传递非空值,则可能在Foo
内发出警告。 (或者它可能会在Foo
的来电者中产生警告。这取决于。)
当然,如果使用代码合同,那么早期删除代码重复是一个坏主意:代码合同确实需要合同存在于公共方法中。
答案 1 :(得分:0)
这不是你问题的完整答案,但评论时间太长了:
Contract.Requires
与验证参数并在验证失败时抛出异常不同。
除非您将代码约定配置为使用运行时检查,否则Contract.Requires
甚至不会将其转换为最终汇编代码(使用dotPeek反编译的Stream
示例相同)
private Task CopyToAsyncInternal(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
Stream.<CopyToAsyncInternal>d__2 stateMachine;
stateMachine.<>__this = this;
stateMachine.destination = destination;
此处没有验证,Stream.<CopyToAsyncInternal>d__2
在以destination
方法访问它之前无法以任何方式验证其MoveNext
字段。
但在私有方法中使用Contract.Requires
可以更好地对合同进行静态分析。