Export (0) Print
Expand All

Choosing Between Properties and Methods

In general, methods represent actions and properties represent data. Properties are meant to be used like fields, meaning that properties should not be computationally complex or produce side effects. When it does not violate the following guidelines, consider using a property, rather than a method, because less experienced developers find properties easier to use.

Consider using a property if the member represents a logical attribute of the type.

For example, BorderStyle is a property because the style of the border is an attribute of a ListView.

Do use a property, rather than a method, if the value of the property is stored in the process memory and the property would just provide access to the value.

The following code example illustrates this guideline. The EmployeeRecord class defines two properties that provide access to private fields. The complete example is shown at the end of this topic.

Public Class EmployeeRecord

    Private employeeIdValue as Integer 
    Private departmentValue as Integer 

    Public Sub New()
    End Sub 

    Public Sub New (id as Integer, departmentId as Integer)
        EmployeeId = id
        Department = departmentId
    End Sub 

    Public Property Department as Integer 
        Get  
            Return departmentValue
        End Get 
        Set 
            departmentValue = value
        End Set 
    End Property 

    Public Property EmployeeId as Integer 
        Get  
            Return employeeIdValue
        End Get 
        Set 
            employeeIdValue = value
        End Set 
    End Property 
    Public Function Clone() as EmployeeRecord
        Return new EmployeeRecord(employeeIdValue, departmentValue)
    End Function 
End Class

Do use a method, rather than a property, in the following situations.

  • The operation is orders of magnitude slower than a field set would be. If you are even considering providing an asynchronous version of an operation to avoid blocking the thread, it is very likely that the operation is too expensive to be a property. In particular, operations that access the network or the file system (other than once for initialization) should most likely be methods, not properties.

  • The operation is a conversion, such as the Object.ToString method.

  • The operation returns a different result each time it is called, even if the parameters do not change. For example, the NewGuid method returns a different value each time it is called.

  • The operation has a significant and observable side effect. Note that populating an internal cache is not generally considered an observable side effect.

  • The operation returns a copy of an internal state (this does not include copies of value type objects returned on the stack).

  • The operation returns an array.

Use a method where the operation returns an array because to preserve the internal array, you would have to return a deep copy of the array, not a reference to the array used by the property. This fact, combined with the fact that developers use properties as though they were fields, can lead to very inefficient code. This is illustrated in the following code example, which returns an array using a property. The complete example is shown at the end of this topic.

Public Class EmployeeData

    Dim data as EmployeeRecord()
    Public Sub New(data as EmployeeRecord())
        Me.data = data
    End Sub 
    Public ReadOnly Property Employees as EmployeeRecord()
        Get 
            Dim newData as EmployeeRecord() = CopyEmployeeRecords()
            Return newData
        End Get 
    End Property 

    Private Function CopyEmployeeRecords() as EmployeeRecord()
        Dim newData(UBound(data)) as EmployeeRecord
        For i as Integer = 0 To UBound(data)
            newData(i) = data(i).Clone()
        Next i
        Console.WriteLine ("EmployeeData: cloned employee data.")
        Return newData
    End Function 
End Class

A developer using this class assumes that the property is no more expensive than a field access and writes application code based on that assumption as shown in the following code example.

Public Class RecordChecker
    Public Shared Function  FindEmployees( _
         dataSource as EmployeeData, _
         department as Integer) as Collection(Of Integer)

        Dim storage as Collection(Of Integer) = new Collection(Of Integer)()
        Console.WriteLine("Record checker: beginning search.")
        For i as Integer = 0 To UBound(dataSource.Employees)
            If dataSource.Employees(i).Department = department
                Console.WriteLine("Record checker: found match at {0}.", i)
                storage.Add(dataSource.Employees(i).EmployeeId)
                Console.WriteLine("Record checker: stored match at {0}.", i)
            Else 
                Console.WriteLine("Record checker: no match at {0}.", i)
            End If 
        Next i
        Return storage
    End Function 
End Class

Note that the Employees property is accessed in each loop iteration and is also accessed when the departments match. Each time the property is accessed, a copy of the employees array is created, used briefly, and then requires garbage collection. By implementing Employees as a method, you indicate to developers that this action is more computationally expensive than accessing a field. Developers are more likely to call a method once and cache the results of the method call to perform their processing.

The following code example shows a complete application that assumes that a property access is computationally inexpensive. The EmployeeData class incorrectly defines a property that returns a copy of an array.

Imports System
Imports System.Collections.ObjectModel

Namespace Examples.DesignGuidelines.Properties
    Public Class EmployeeRecord

        Private employeeIdValue as Integer 
        Private departmentValue as Integer 

        Public Sub New()
        End Sub 

        Public Sub New (id as Integer, departmentId as Integer)
            EmployeeId = id
            Department = departmentId
        End Sub 

        Public Property Department as Integer 
            Get  
                Return departmentValue
            End Get 
            Set 
                departmentValue = value
            End Set 
        End Property 

        Public Property EmployeeId as Integer 
            Get  
                Return employeeIdValue
            End Get 
            Set 
                employeeIdValue = value
            End Set 
        End Property 
        Public Function Clone() as EmployeeRecord
            Return new EmployeeRecord(employeeIdValue, departmentValue)
        End Function 
    End Class 

Public Class EmployeeData

    Dim data as EmployeeRecord()
    Public Sub New(data as EmployeeRecord())
        Me.data = data
    End Sub 
    Public ReadOnly Property Employees as EmployeeRecord()
        Get 
            Dim newData as EmployeeRecord() = CopyEmployeeRecords()
            Return newData
        End Get 
    End Property 

    Private Function CopyEmployeeRecords() as EmployeeRecord()
        Dim newData(UBound(data)) as EmployeeRecord
        For i as Integer = 0 To UBound(data)
            newData(i) = data(i).Clone()
        Next i
        Console.WriteLine ("EmployeeData: cloned employee data.")
        Return newData
    End Function 
End Class 

Public Class RecordChecker
    Public Shared Function  FindEmployees( _
         dataSource as EmployeeData, _
         department as Integer) as Collection(Of Integer)

        Dim storage as Collection(Of Integer) = new Collection(Of Integer)()
        Console.WriteLine("Record checker: beginning search.")
        For i as Integer = 0 To UBound(dataSource.Employees)
            If dataSource.Employees(i).Department = department
                Console.WriteLine("Record checker: found match at {0}.", i)
                storage.Add(dataSource.Employees(i).EmployeeId)
                Console.WriteLine("Record checker: stored match at {0}.", i)
            Else 
                Console.WriteLine("Record checker: no match at {0}.", i)
            End If 
        Next i
        Return storage
    End Function 
End Class 
    Public Class Tester
        Public Shared Sub Main()
            Dim records(2) as EmployeeRecord
            Dim r0 as EmployeeRecord = new EmployeeRecord()
            r0.EmployeeId = 1
            r0.Department = 100
            records(0) = r0
            Dim r1 as EmployeeRecord = new EmployeeRecord()
            r1.EmployeeId = 2
            r1.Department = 100
            records(1) = r1
            Dim r2 as EmployeeRecord = new EmployeeRecord()
            r2.EmployeeId = 3
            r2.Department = 101
            records(2) = r2
            Dim empData as EmployeeData = new EmployeeData(records)
            Dim hits as Collection(Of Integer)= _ 
                RecordChecker.FindEmployees(empData, 100)
            For Each i as Integer In hits
                Console.WriteLine("found employee {0}", i)
            Next i
        End Sub 
    End Class 
End Namespace

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.

Community Additions

ADD
Show:
© 2014 Microsoft