Auswählen zwischen Eigenschaften und Methoden

Grundsätzlich stellen Methoden Aktionen und Eigenschaften Daten dar. Eigenschaften sollen wie Felder verwendet werden und daher mit keinem großen Rechenaufwand verbunden sein und keine Nebeneffekte erzeugen. Sofern die folgenden Richtlinien nicht verletzt werden, verwenden Sie eine Eigenschaft und keine Methode, weil die Verwendung von Eigenschaften für weniger erfahrene Entwickler einfacher ist.

Verwenden Sie eine Eigenschaft, wenn der Member ein logisches Attribut des Typs darstellt.

Beispielsweise ist BorderStyle eine Eigenschaft, weil das Format des Rahmens ein Attribut einer ListView ist.

Verwenden Sie eine Eigenschaft und keine Methode, wenn der Wert der Eigenschaft im Prozessspeicher gespeichert wird und die Eigenschaft nur den Zugriff auf den Wert ermöglicht.

Im folgenden Codebeispiel wird diese Richtlinie veranschaulicht. Die EmployeeRecord-Klasse definiert zwei Eigenschaften, die den Zugriff auf private Felder ermöglichen. Das vollständige Beispiel wird am Ende dieses Themas dargestellt.

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);
    }
};

Verwenden Sie in den folgenden Situationen eine Methode und keine Eigenschaft.

  • Die Operation ist weitaus langsamer als das Festlegen eines Feldes. Selbst wenn Sie erwägen, eine asynchrone Version einer Operation bereitzustellen, um das Blockieren des Threads zu vermeiden, ist die Operation mit hoher Wahrscheinlichkeit zu aufwendig für eine Eigenschaft. Insbesondere Operationen, die auf das Netzwerk oder Dateisystem zugreifen (nicht nur einmal für die Initialisierung), sollten mit hoher Wahrscheinlichkeit Methoden und keine Eigenschaften sein.

  • Die Operation ist eine Konvertierung, z. B. Object.ToString method.

  • Die Operation gibt bei jedem Aufruf ein anderes Ergebnis zurück, selbst wenn sich die Parameter nicht ändern. Beispielsweise gibt die NewGuid-Methode bei jedem Aufruf einen anderen Wert zurück.

  • Die Operation hat bedeutende und wahrnehmbare Nebeneffekte. Beachten Sie, dass das Auffüllen eines internen Caches im Allgemeinen nicht als wahrnehmbarer Nebeneffekt angesehen wird.

  • Die Operation gibt eine Kopie eines internen Zustands zurück (dies umfasst nicht Kopien von Werttypobjekten, die auf dem Stapel zurückgegeben werden).

  • Die Operation gibt ein Array zurück.

Verwenden Sie eine Methode, wenn die Operation ein Array zurückgibt, weil Sie eine tiefe Kopie des Arrays und keinen Verweis auf das von der Eigenschaft verwendete Array zurückgeben müssen, um das interne Array beizubehalten. Dies kann zusammen mit der Tatsache, dass Entwickler Eigenschaften wie Felder behandeln, zu äußerst ineffizientem Code führen. Im folgenden Codebeispiel, in dem ein Array mithilfe einer Eigenschaft zurückgegeben wird, wird dies veranschaulicht. Das vollständige Beispiel wird am Ende dieses Themas dargestellt.

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;
    }
};

Ein diese Klasse verwendender Entwickler geht davon aus, dass die Eigenschaft nicht aufwendiger als der Zugriff auf ein Feld ist und schreibt auf der Grundlage dieser Annahme Anwendungscode, wie im folgenden Codebeispiel veranschaulicht.

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;
    }
};

Beachten Sie, dass bei jeder Schleifeniteration und auch bei der Übereinstimmung der Abteilungen auf die Employees-Eigenschaft zugegriffen wird. Bei jedem Zugriff auf die Eigenschaft wird eine Kopie des Mitarbeiterarrays erstellt, kurz verwendet, und anschließend ist die Garbage Collection für die Kopie erforderlich. Durch Implementieren von Employees als Methode können Sie Entwickler darauf aufmerksam machen, dass diese Aktion rechenintensiver als der Zugriff auf ein Feld ist. Entwickler rufen mit größerer Wahrscheinlichkeit eine Methode einmal auf und zwischenspeichern die Ergebnisse des Methodenaufrufs, um die Verarbeitung auszuführen.

Beispiel

Im folgenden Codebeispiel wird eine vollständige Anwendung veranschaulicht, die davon ausgeht, dass der Zugriff auf eine Eigenschaft wenig Rechenaufwand erfordert. Die EmployeeData-Klasse definiert fälschlicherweise eine Eigenschaft, die eine Kopie eines Arrays zurückgibt.

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);
            }
        }
    };
}}}

Copyright für einzelne Teile 2005 Microsoft Corporation. Alle Rechte vorbehalten.

Copyright für einzelne Teile Addison-Wesley Corporation. Alle Rechte vorbehalten.

Weitere Informationen zu Entwurfsrichtlinien finden Sie unter „Framework-Entwurfs-Richtlinien: Idiome, Konventionen und Muster für wiederverwendbare .NET-Bibliotheken von Krzysztof Cwalina“ book und Brad Abrams, veröffentlicht von Addison-Wesley, 2005.

Siehe auch

Konzepte

Eigenschaftenentwurf

Weitere Ressourcen

Entwurfsrichtlinien für Member

Entwurfsrichtlinien zum Entwickeln von Klassenbibliotheken