CA1024: Use properties where appropriate

TypeName

UsePropertiesWhereAppropriate

CheckId

CA1024

Category

Microsoft.Design

Breaking Change

Breaking

Cause

A public or protected method has a name that starts with Get, takes no parameters, and returns a value that is not an array.

Rule Description

In most cases, properties represent data and methods perform actions. Properties are accessed like fields, which makes them easier to use. A method is a good candidate to become a property if one of these conditions is present:

  • Takes no arguments and returns the state information of an object.

  • Accepts a single argument to set some part of the state of an object.

Properties should behave as if they are fields; if the method cannot, it should not be changed to a property. Methods are better than properties in the following situations:

  • The method performs a time-consuming operation. The method is perceivably slower than the time that is required to set or get the value of a field.

  • The method performs a conversion. Accessing a field does not return a converted version of the data that it stores.

  • The Get method has an observable side effect. Retrieving the value of a field does not produce any side effects.

  • The order of execution is important. Setting the value of a field does not rely on the occurrence of other operations.

  • Calling the method two times in succession creates different results.

  • The method is static but returns an object that can be changed by the caller. Retrieving the value of a field does not allow the caller to change the data that is stored by the field.

  • The method returns an array.

How to Fix Violations

To fix a violation of this rule, change the method to a property.

When to Suppress Warnings

Suppress a warning from this rule if the method meets at least one of the previously listed criteria.

Controlling Property Expansion in the Debugger

One reason programmers avoid using a property is because they do not want the debugger to auto-expand it. For example, the property might involve allocating a large object or calling a P/Invoke, but it might not actually have any observable side effects.

You can prevent the debugger from auto-expanding properties by applying System.Diagnostics.DebuggerBrowsableAttribute. The following example shows this attribute being applied to an instance property.

Imports System 
Imports System.Diagnostics 

Namespace Microsoft.Samples 

    Public Class TestClass 

        ' [...] 

        <DebuggerBrowsable(DebuggerBrowsableState.Never)> _ 
        Public ReadOnly Property LargeObject() As LargeObject 
            Get 
                ' Allocate large object 
                ' [...] 
            End Get 
        End Property 

    End Class 

End Namespace
using System; 
using System.Diagnostics; 

namespace Microsoft.Samples 
{ 
    public class TestClass 
    { 
        // [...] 

        [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
        public LargeObject LargeObject 
        { 
            get 
            { 
                // Allocate large object 
                // [...] 

        }
    }
}

Example

The following example contains several methods that should be converted to properties, and several that should not because they do not behave like fields.

using System;
using System.Globalization;
using System.Collections;
namespace DesignLibrary
{
   // Illustrates the behavior of rule:  
   //  UsePropertiesWhereAppropriate. 

   public class Appointment
   {
      static long nextAppointmentID;
      static double[] discountScale = {5.0, 10.0, 33.0};
      string customerName;
      long customerID;
      DateTime when;

      // Static constructor. 
      static Appointment()
      {
         // Initializes the static variable for Next appointment ID.
      }

      // This method will violate the rule, but should not be a property. 
      // This method has an observable side effect.  
      // Calling the method twice in succession creates different results. 
      public static long GetNextAvailableID()
      {
         nextAppointmentID++;
         return nextAppointmentID - 1;
      }

      // This method will violate the rule, but should not be a property. 
      // This method performs a time-consuming operation.  
      // This method returns an array. 

      public Appointment[] GetCustomerHistory()
      {
         // Connect to a database to get the customer's appointment history. 
         return LoadHistoryFromDB(customerID);
      }

      // This method will violate the rule, but should not be a property. 
      // This method is static but returns a mutable object. 
      public static double[] GetDiscountScaleForUpdate()
      {
         return discountScale;
      }

      // This method will violate the rule, but should not be a property. 
      // This method performs a conversion. 
      public string GetWeekDayString()
      {
         return DateTimeFormatInfo.CurrentInfo.GetDayName(when.DayOfWeek);
      }

      // These methods will violate the rule, and should be properties. 
      // They each set or return a piece of the current object's state. 

      public DayOfWeek GetWeekDay ()
      {
         return when.DayOfWeek;
      }

      public void  SetCustomerName (string customerName)
      {
         this.customerName = customerName;
      }
      public string GetCustomerName ()
      {
         return customerName;
      }

     public void SetCustomerID (long customerID)
      {
         this.customerID = customerID;
      }

      public long GetCustomerID ()
      {
         return customerID;
      }

      public void SetScheduleTime (DateTime when)
      {
         this.when = when;
      }

      public DateTime GetScheduleTime ()
      {
         return when;
      }

      // Time-consuming method that is called by GetCustomerHistory.
      Appointment[] LoadHistoryFromDB(long customerID)
      {
         ArrayList records = new ArrayList();
         // Load from database. 
         return (Appointment[])records.ToArray();
      }
   }
}