Compartir a través de


Elegir entre propiedades y métodos

En general, los métodos representan acciones y las propiedades representan datos. Las propiedades están pensadas para utilizarse como campos, en el sentido de que las propiedades no deberían ser computacionalmente complejas ni generar efectos secundarios. Cuando no se infrinjan las siguientes instrucciones, considere la posibilidad de usar una propiedad en lugar de un método, porque a los desarrolladores con menos experiencia les es más fácil utilizar las propiedades.

Considere utilizar una propiedad si el miembro representa un atributo lógico del tipo.

Por ejemplo, BorderStyle es una propiedad porque el estilo del borde es un atributo de una ListView.

Utilice una propiedad, en lugar de un método, si el valor de la propiedad está almacenado en la memoria del proceso y la propiedad sólo proporcionaría acceso al valor.

En el siguiente ejemplo de código se ilustra esta instrucción. La clase EmployeeRecord define dos propiedades que proporcionan acceso a los campos privados. El ejemplo completo se muestra al final de este tema.

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 EmployeeRecord
{
    private int employeeId;
    private int department;
    public EmployeeRecord()
    {
    }
    public  EmployeeRecord (int id, int departmentId)
    {
        EmployeeId = id;
        Department = departmentId;
    }
    public int Department
    {
        get {return department;}
        set {department = value;}
    }
    public int EmployeeId
    {
        get {return employeeId;}
        set {employeeId = value;}
    }
    public EmployeeRecord Clone()
    {
        return new EmployeeRecord(employeeId, department);
    }
}
public ref class EmployeeRecord
{
private:
    int employeeId;
    int department;

public:
    EmployeeRecord()
    {
    }

    EmployeeRecord(int id, int departmentId)
    {
        EmployeeId = id;
        Department = departmentId;
    }

    property int Department
    {
        int get() {return department;}
        void set(int value) {department = value;}
    }

    property int EmployeeId
    {
        int get() {return employeeId;}
        void set(int value) {employeeId = value;}
    }

    EmployeeRecord^ Clone()
    {
        return gcnew EmployeeRecord(employeeId, department);
    }
};

Utilice un método, en lugar de una propiedad, en las siguientes situaciones.

  • La operación es más lenta en varios órdenes de magnitud de lo que sería el establecimiento de un campo. Si está considerando proporcionar una versión asincrónica de una operación para evitar que se bloquee el subproceso, es muy probable que la operación consuma demasiados recursos para ser una propiedad. En particular, las operaciones que tienen acceso a la red o al sistema de archivos (salvo una vez para la inicialización) probablemente deberían ser métodos, no propiedades.

  • La operación es una conversión, como el Object.ToString method.

  • La operación devuelve un resultado distinto cada vez que se la llama, aun cuando los parámetros no cambian. Por ejemplo, el método NewGuid devuelve un valor diferente cada vez que se lo llama.

  • La operación tiene un efecto secundario significativo y notable. Observe que rellenar una caché interna no se suele considerar como un efecto secundario notable.

  • La operación devuelve una copia de un estado interno (esto no incluye las copias de objetos de tipo de valor devueltas a la pila).

  • La operación devuelve una matriz.

Utilice un método cuando la operación devuelve una matriz porque, para conservar una matriz interna, tendría que devolver una copia profunda de la matriz, no una referencia a la matriz usada por la propiedad. Este hecho, combinado con el hecho de que los desarrolladores utilizan las propiedades como si fueran campos, puede llevar a que el código sea muy ineficaz. Esto se puede ver en el siguiente ejemplo de código, que devuelve una matriz mediante una propiedad. El ejemplo completo se muestra al final de este tema.

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 EmployeeData
{
    EmployeeRecord[] data;
    public EmployeeData(EmployeeRecord[] data)
    {
        this.data = data;
    }
    public EmployeeRecord[] Employees
    {
        get 
        {
            EmployeeRecord[] newData = CopyEmployeeRecords();
            return newData;
        }
    }
    EmployeeRecord[] CopyEmployeeRecords()
    {
        EmployeeRecord[] newData = new EmployeeRecord[data.Length];
        for(int i = 0; i< data.Length; i++)
        {
            newData[i] = data[i].Clone();
        }
        Console.WriteLine ("EmployeeData: cloned employee data.");
        return newData;
    }
}
public ref class EmployeeData
{
private:
    array<EmployeeRecord^>^ data;

public:
    EmployeeData(array<EmployeeRecord^>^ data)
    {
        this->data = data;
    }

    property array<EmployeeRecord^>^ Employees
    {
        array<EmployeeRecord^>^ get()
        {
            array<EmployeeRecord^>^ newData = CopyEmployeeRecords();
            return newData;
        }
    }

private:
    array<EmployeeRecord^>^ CopyEmployeeRecords()
    {
        array<EmployeeRecord^>^ newData = gcnew array<EmployeeRecord^>(data->Length);
        for(int i = 0; i< data->Length; i++)
        {
            newData[i] = data[i]->Clone();
        }
        Console::WriteLine ("EmployeeData: cloned employee data.");

        return newData;
    }
};

Un desarrollador que utilice esta clase da por supuesto que la propiedad no consume más recursos que el acceso a los campos y escribe código de aplicación basándose en ese supuesto como se puede ver en el ejemplo de código siguiente.

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 RecordChecker
{
    public static Collection<int> FindEmployees(EmployeeData dataSource, 
             int department)
    {
        Collection<int> storage = new Collection<int>();
        Console.WriteLine("Record checker: beginning search.");
        for (int i = 0; i < dataSource.Employees.Length; i++)
        {
            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);
            }
        }
        return storage;
    }
}
public class RecordChecker
{
public:
    static Collection<int>^ FindEmployees(EmployeeData^ dataSource,
             int department)
    {
        Collection<int>^ storage = gcnew Collection<int>();
        Console::WriteLine("Record checker: beginning search.");
        for (int i = 0; i < dataSource->Employees->Length; i++)
        {
            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);
            }
        }
        return storage;
    }
};

Observe que se tiene acceso a la propiedad Employees en cada iteración del bucle y también se obtiene acceso a ella cuando coinciden los departamentos. Cada vez que se tiene acceso a la propiedad, se crea una copia de la matriz de empleados, se utiliza brevemente y, a continuación, requiere la recolección de elementos no utilizados. Al implementar Employees como un método, se indica a los desarrolladores que esta acción requiere más computación que tener acceso a un campo. Los desarrolladores tienen más tendencia a llamar una vez a un método y almacenar en memoria caché los resultados de la llamada al método para realizar su procesamiento.

Ejemplo

El ejemplo de código siguiente muestra una aplicación completa que presupone que un acceso a las propiedades requiere menos computación. La clase EmployeeData define incorrectamente una propiedad que devuelve una copia de una matriz.

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
using System;
using System.Collections.ObjectModel;
namespace Examples.DesignGuidelines.Properties
{
    public class EmployeeRecord
    {
        private int employeeId;
        private int department;
        public EmployeeRecord()
        {
        }
        public  EmployeeRecord (int id, int departmentId)
        {
            EmployeeId = id;
            Department = departmentId;
        }
        public int Department
        {
            get {return department;}
            set {department = value;}
        }
        public int EmployeeId
        {
            get {return employeeId;}
            set {employeeId = value;}
        }
        public EmployeeRecord Clone()
        {
            return new EmployeeRecord(employeeId, department);
        }
    }

public class EmployeeData
{
    EmployeeRecord[] data;
    public EmployeeData(EmployeeRecord[] data)
    {
        this.data = data;
    }
    public EmployeeRecord[] Employees
    {
        get 
        {
            EmployeeRecord[] newData = CopyEmployeeRecords();
            return newData;
        }
    }
    EmployeeRecord[] CopyEmployeeRecords()
    {
        EmployeeRecord[] newData = new EmployeeRecord[data.Length];
        for(int i = 0; i< data.Length; i++)
        {
            newData[i] = data[i].Clone();
        }
        Console.WriteLine ("EmployeeData: cloned employee data.");
        return newData;
    }
}

public class RecordChecker
{
    public static Collection<int> FindEmployees(EmployeeData dataSource, 
             int department)
    {
        Collection<int> storage = new Collection<int>();
        Console.WriteLine("Record checker: beginning search.");
        for (int i = 0; i < dataSource.Employees.Length; i++)
        {
            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);
            }
        }
        return storage;
    }
}
    public class Tester
    {
        public static void Main()
        {
            EmployeeRecord[] records  = new EmployeeRecord[3];
            EmployeeRecord r0  = new EmployeeRecord();
            r0.EmployeeId = 1;
            r0.Department = 100;
            records[0] = r0;
            EmployeeRecord r1  = new EmployeeRecord();
            r1.EmployeeId = 2;
            r1.Department = 100;
            records[1] = r1;
            EmployeeRecord r2  = new EmployeeRecord();
            r2.EmployeeId = 3;
            r2.Department = 101;
            records[2] = r2;
            EmployeeData empData = new EmployeeData(records);
            Collection<int> hits = RecordChecker.FindEmployees(empData, 100);
            foreach (int i in hits)
            {
                Console.WriteLine("found employee {0}", i);
            }
        }
    }
}

using namespace System;
using namespace System::Collections::ObjectModel;

namespace Examples { namespace DesignGuidelines { namespace Properties
{
    public ref class EmployeeRecord
    {
    private:
        int employeeId;
        int department;

    public:
        EmployeeRecord()
        {
        }

        EmployeeRecord(int id, int departmentId)
        {
            EmployeeId = id;
            Department = departmentId;
        }

        property int Department
        {
            int get() {return department;}
            void set(int value) {department = value;}
        }

        property int EmployeeId
        {
            int get() {return employeeId;}
            void set(int value) {employeeId = value;}
        }

        EmployeeRecord^ Clone()
        {
            return gcnew EmployeeRecord(employeeId, department);
        }
    };

    public ref class EmployeeData
    {
    private:
        array<EmployeeRecord^>^ data;

    public:
        EmployeeData(array<EmployeeRecord^>^ data)
        {
            this->data = data;
        }

        property array<EmployeeRecord^>^ Employees
        {
            array<EmployeeRecord^>^ get()
            {
                array<EmployeeRecord^>^ newData = CopyEmployeeRecords();
                return newData;
            }
        }

    private:
        array<EmployeeRecord^>^ CopyEmployeeRecords()
        {
            array<EmployeeRecord^>^ newData = gcnew array<EmployeeRecord^>(data->Length);
            for(int i = 0; i< data->Length; i++)
            {
                newData[i] = data[i]->Clone();
            }
            Console::WriteLine ("EmployeeData: cloned employee data.");

            return newData;
        }
    };

    public class RecordChecker
    {
    public:
        static Collection<int>^ FindEmployees(EmployeeData^ dataSource,
                 int department)
        {
            Collection<int>^ storage = gcnew Collection<int>();
            Console::WriteLine("Record checker: beginning search.");
            for (int i = 0; i < dataSource->Employees->Length; i++)
            {
                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);
                }
            }
            return storage;
        }
    };

    public ref class Tester
    {
    public:
        static void Main()
        {
            array<EmployeeRecord^>^ records = gcnew array<EmployeeRecord^>(3);
            EmployeeRecord^ r0  = gcnew EmployeeRecord();
            r0->EmployeeId = 1;
            r0->Department = 100;
            records[0] = r0;
            EmployeeRecord^ r1  = gcnew EmployeeRecord();
            r1->EmployeeId = 2;
            r1->Department = 100;
            records[1] = r1;
            EmployeeRecord^ r2  = gcnew EmployeeRecord();
            r2->EmployeeId = 3;
            r2->Department = 101;
            records[2] = r2;
            EmployeeData^ empData = gcnew EmployeeData(records);
            Collection<int>^ hits = RecordChecker::FindEmployees(empData, 100);
            for each (int i in hits)
            {
                Console::WriteLine("found employee {0}", i);
            }
        }
    };
}}}

Portions Copyright 2005 Microsoft Corporation. Reservados todos los derechos.

Portions Copyright Addison-Wesley Corporation. Reservados todos los derechos.

Para obtener más información sobre las directrices de diseño, consulte “las instrucciones de diseño de Framework: Convenciones, frases realizadas y modelos para libro de bibliotecas reutilizables de .NET” de Krzysztof Cwalina y Brad Abrams, publicados por Addison-Wesley, 2005.

Vea también

Conceptos

Diseño de propiedades

Otros recursos

Instrucciones de diseño de miembros

Instrucciones de diseño para desarrollar bibliotecas de clases