Consider passing base types as parameters

TypeName

ConsiderPassingBaseTypesAsParameters

CheckId

CA1011

Category

Microsoft.Design

Breaking Change

Breaking

Cause

A method declaration includes a formal parameter that is a derived type, and the method only calls members of the parameter's base type.

Rule Description

When a base type is specified as a parameter in a method declaration, any type derived from the base type can be passed as the corresponding argument to the method. When the argument is used inside the method body, the specific method that is executed depends on the type of the argument. If the additional functionality provided by the derived type is not required, use of the base type allows the method to be more widely utilized.

How to Fix Violations

To fix a violation of this rule, change the type of the parameter to its base type.

When to Exclude Warnings

It is safe to exclude a warning from this rule if the method requires the specific functionality provided by the derived type or to enforce that only the derived type, or a more derived type, is passed to the method. In this case, the code will be more robust due to the strong type checking provided by the compiler and runtime.

Example

The following example shows a method, ManipulateFileStream, that can only be used with a FileStream object, which violates this rule. A second method, ManipulateAnyStream, satisfies the rule by replacing the FileStream parameter with a Stream.

Imports System
Imports System.IO

Namespace DesignLibrary

    Public Class StreamUser

        Sub ManipulateFileStream(ByVal stream As IO.FileStream)
            If stream Is Nothing Then Throw New ArgumentNullException("stream")

            Dim anInteger As Integer = stream.ReadByte()
            While (anInteger <> -1)
                ' Do something.
                anInteger = stream.ReadByte()
            End While
        End Sub

        Sub ManipulateAnyStream(ByVal anyStream As IO.Stream)
            If anyStream Is Nothing Then Throw New ArgumentNullException("anyStream")

            Dim anInteger As Integer = anyStream.ReadByte()
            While (anInteger <> -1)
                ' Do something.
                anInteger = anyStream.ReadByte()
            End While
        End Sub
    End Class


   Public Class TestStreams
   
      Shared Sub Main()
            Dim someStreamUser As New StreamUser()
            Dim testFileStream As New FileStream( _
               "test.dat", FileMode.OpenOrCreate)
            Dim testMemoryStream As New MemoryStream(New Byte() {})

            ' Cannot be used with testMemoryStream.
            someStreamUser.ManipulateFileStream(testFileStream)
            someStreamUser.ManipulateAnyStream(testFileStream)
            someStreamUser.ManipulateAnyStream(testMemoryStream)
            testFileStream.Close()
        End Sub
   End Class
End Namespace
using System;
using System.IO;

namespace DesignLibrary
{
   public class StreamUser
   {
      int anInteger;

      public void ManipulateFileStream(FileStream stream)
      {
         while((anInteger = stream.ReadByte()) != -1)
         {
            // Do something.
         }
      }

      public void ManipulateAnyStream(Stream anyStream)
      {
         while((anInteger = anyStream.ReadByte()) != -1)
         {
            // Do something.
         }
      }
   }

   class TestStreams
   {
      static void Main()
      {
         StreamUser someStreamUser = new StreamUser();
         MemoryStream testMemoryStream = new MemoryStream(new byte[] {});
         using(FileStream testFileStream = 
                  new FileStream("test.dat", FileMode.OpenOrCreate))
         {
            // Cannot be used with testMemoryStream.
            someStreamUser.ManipulateFileStream(testFileStream);

            someStreamUser.ManipulateAnyStream(testFileStream);
            someStreamUser.ManipulateAnyStream(testMemoryStream);
         }
      }
   }
}
using namespace System;
using namespace System::IO;

namespace DesignLibrary
{
   public ref class StreamUser
   {
      int anInteger;

   public:
      void ManipulateFileStream(FileStream^ stream)
      {
         while((anInteger = stream->ReadByte()) != -1)
         {
            // Do something.
         }
      }

      void ManipulateAnyStream(Stream^ anyStream)
      {
         while((anInteger = anyStream->ReadByte()) != -1)
         {
            // Do something.
         }
      }
   };
}

using namespace DesignLibrary;

static void main()
{
   StreamUser^ someStreamUser = gcnew StreamUser();
   FileStream^ testFileStream = 
      gcnew FileStream("test.dat", FileMode::OpenOrCreate);
   MemoryStream^ testMemoryStream = 
      gcnew MemoryStream(gcnew array<Byte>{});

   // Cannot be used with testMemoryStream.
   someStreamUser->ManipulateFileStream(testFileStream);

   someStreamUser->ManipulateAnyStream(testFileStream);
   someStreamUser->ManipulateAnyStream(testMemoryStream);

   testFileStream->Close();
}

While this rule currently fires on virtual methods in Visual Studio 2005, in future versions of Visual Studio this will be changed so that following will no longer cause ConsiderPassingBaseTypesAsParameters to fire.

Members should not expose certain concrete types