由于C#中的Reflection,验证私有方法的参数

时间:2015-02-27 07:31:15

标签: c# validation reflection

我想知道是否有必要再次验证传递给私有类方法的参数,不使用Reflection只会被同一个类中的公共方法调用。

如果私有方法实例化一个需要处理的对象(在由于错误的参数而出现问题之前),则可能抛出异常(在这种情况下,对象将被处理掉),对吗?

我正在查看.NET的一些源代码(主要是StringStream类)。我发现一些私有方法参数是通过合同验证的,但在其他方面没有发生检查。

不再验证参数的代码(取自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
    }

哪种是最佳做法,还是取决于具体情况 - 它是一个仅在特定项目中使用的类库,还是会在事先不知道的不同环境中使用?

2 个答案:

答案 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可以更好地对合同进行静态分析。